mirror of
https://github.com/trailofbits/manticore.git
synced 2026-10-11 22:29:55 +00:00
Blacken Manticore (#1438)
* Add black to CI tests I'd rather have blackening be an optional check, but this will work for now * Yep, messed up Travis Again, sorry about the commit spam * Bump accepted line length to 120 Going to auto-format at 100 but will allow 20% overruns * Blacken * Cap line length at 100 rip my undisciplined coding habits * update black on the fly if necessary * Ignore CC errors * Only check modified files * Only python files * Re-blacken post merge * Re-Blacken * Fix deleted files * Explicitly invoke python The +x must not have gotten commited * Forgot to blacken again Hoisted on my own petard * Re-blacken
This commit is contained in:
@@ -48,6 +48,10 @@ plugins:
|
||||
enabled: false
|
||||
E701:
|
||||
enabled: false
|
||||
E203:
|
||||
enabled: false
|
||||
W503:
|
||||
enabled: false
|
||||
sonar-python:
|
||||
enabled: false
|
||||
config:
|
||||
|
||||
@@ -7,6 +7,7 @@ python:
|
||||
- 3.6.6
|
||||
|
||||
stages:
|
||||
- format
|
||||
- prepare
|
||||
- test
|
||||
- submit
|
||||
@@ -36,6 +37,10 @@ cache:
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: format
|
||||
env: TEST_TYPE=format
|
||||
script:
|
||||
- git diff --name-only $TRAVIS_COMMIT_RANGE | python3 scripts/pyfile_exists.py | xargs black -t py36 -l 100 --check
|
||||
- stage: prepare
|
||||
env: TEST_TYPE=env
|
||||
script:
|
||||
|
||||
@@ -30,6 +30,10 @@ more documentation, look [here](https://guides.github.com/activities/forking/).
|
||||
|
||||
Some pull request guidelines:
|
||||
|
||||
- We use the [`black`](https://black.readthedocs.io/en/stable/index.html) auto-formatter
|
||||
to enforce style conventions in Manticore. To ensure your code is properly
|
||||
formatted, run `black -t py36 -l 100 .` in the manticore directory before
|
||||
committing.
|
||||
- Minimize irrelevant changes (formatting, whitespace, etc) to code that would
|
||||
otherwise not be touched by this patch. Save formatting or style corrections
|
||||
for a separate pull request that does not make any semantic changes.
|
||||
|
||||
+25
-26
@@ -30,33 +30,33 @@
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc']
|
||||
extensions = ["sphinx.ext.autodoc"]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
source_suffix = ".rst"
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
master_doc = "index"
|
||||
|
||||
# General information about the project.
|
||||
project = u'Manticore'
|
||||
copyright = u'2017, Trail of Bits'
|
||||
author = u'Trail of Bits'
|
||||
project = "Manticore"
|
||||
copyright = "2019, Trail of Bits"
|
||||
author = "Trail of Bits"
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'0.1.0'
|
||||
version = "0.2.5"
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'0.1.0'
|
||||
release = "0.2.5"
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
@@ -68,10 +68,10 @@ language = None
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
pygments_style = "sphinx"
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
@@ -82,7 +82,7 @@ todo_include_todos = False
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
html_theme = "alabaster"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
@@ -93,13 +93,13 @@ html_theme = 'alabaster'
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
html_static_path = ["_static"]
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Manticoredoc'
|
||||
htmlhelp_basename = "Manticoredoc"
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
@@ -108,15 +108,12 @@ latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
@@ -126,8 +123,7 @@ latex_elements = {
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'Manticore.tex', u'Manticore Documentation',
|
||||
u'Trail of Bits', 'manual'),
|
||||
(master_doc, "Manticore.tex", "Manticore Documentation", "Trail of Bits", "manual")
|
||||
]
|
||||
|
||||
|
||||
@@ -135,10 +131,7 @@ latex_documents = [
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'manticore', u'Manticore Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
man_pages = [(master_doc, "manticore", "Manticore Documentation", [author], 1)]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
@@ -147,9 +140,15 @@ man_pages = [
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'Manticore', u'Manticore Documentation',
|
||||
author, 'Manticore', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
(
|
||||
master_doc,
|
||||
"Manticore",
|
||||
"Manticore Documentation",
|
||||
author,
|
||||
"Manticore",
|
||||
"One line description of project.",
|
||||
"Miscellaneous",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
|
||||
+31
-31
@@ -5,53 +5,53 @@
|
||||
import pyevmasm as ea
|
||||
from binascii import hexlify
|
||||
|
||||
|
||||
def printi(instruction):
|
||||
print(f'Instruction: {instruction}')
|
||||
print('\tdescription:', instruction.description)
|
||||
print('\tgroup:', instruction.group)
|
||||
print('\taddress:', instruction.pc)
|
||||
print('\tsize:', instruction.size)
|
||||
print('\thas_operand:', instruction.has_operand)
|
||||
print('\toperand_size:', instruction.operand_size)
|
||||
print('\toperand:', instruction.operand)
|
||||
print('\tsemantics:', instruction.semantics)
|
||||
print('\tpops:', instruction.pops)
|
||||
print('\tpushes:', instruction.pushes)
|
||||
print(f'\tbytes: 0x'+hexlify(instruction.bytes).decode())
|
||||
print('\twrites to stack:', instruction.writes_to_stack)
|
||||
print('\treads from stack:', instruction.reads_from_stack)
|
||||
print('\twrites to memory:', instruction.writes_to_memory)
|
||||
print('\treads from memory:', instruction.reads_from_memory)
|
||||
print('\twrites to storage:', instruction.writes_to_storage)
|
||||
print('\treads from storage:', instruction.reads_from_storage)
|
||||
print('\tis terminator', instruction.is_terminator)
|
||||
print(f"Instruction: {instruction}")
|
||||
print("\tdescription:", instruction.description)
|
||||
print("\tgroup:", instruction.group)
|
||||
print("\taddress:", instruction.pc)
|
||||
print("\tsize:", instruction.size)
|
||||
print("\thas_operand:", instruction.has_operand)
|
||||
print("\toperand_size:", instruction.operand_size)
|
||||
print("\toperand:", instruction.operand)
|
||||
print("\tsemantics:", instruction.semantics)
|
||||
print("\tpops:", instruction.pops)
|
||||
print("\tpushes:", instruction.pushes)
|
||||
print(f"\tbytes: 0x" + hexlify(instruction.bytes).decode())
|
||||
print("\twrites to stack:", instruction.writes_to_stack)
|
||||
print("\treads from stack:", instruction.reads_from_stack)
|
||||
print("\twrites to memory:", instruction.writes_to_memory)
|
||||
print("\treads from memory:", instruction.reads_from_memory)
|
||||
print("\twrites to storage:", instruction.writes_to_storage)
|
||||
print("\treads from storage:", instruction.reads_from_storage)
|
||||
print("\tis terminator", instruction.is_terminator)
|
||||
|
||||
|
||||
instruction = ea.disassemble_one('\x60\x10')
|
||||
instruction = ea.disassemble_one("\x60\x10")
|
||||
printi(instruction)
|
||||
|
||||
instruction = ea.assemble_one('PUSH1 0x10')
|
||||
instruction = ea.assemble_one("PUSH1 0x10")
|
||||
printi(instruction)
|
||||
|
||||
for instruction in ea.disassemble_all('\x30\x31'):
|
||||
for instruction in ea.disassemble_all("\x30\x31"):
|
||||
printi(instruction)
|
||||
|
||||
for instruction in ea.assemble_all('ADDRESS\nBALANCE'):
|
||||
for instruction in ea.assemble_all("ADDRESS\nBALANCE"):
|
||||
printi(instruction)
|
||||
|
||||
|
||||
#High level simple assembler/disassembler
|
||||
print(ea.assemble_hex(
|
||||
"""PUSH1 0x60
|
||||
# High level simple assembler/disassembler
|
||||
print(
|
||||
ea.assemble_hex(
|
||||
"""PUSH1 0x60
|
||||
BLOCKHASH
|
||||
MSTORE
|
||||
PUSH1 0x2
|
||||
PUSH2 0x100
|
||||
"""
|
||||
))
|
||||
|
||||
|
||||
print(ea.disassemble_hex('0x606040526002610100'))
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
print(ea.disassemble_hex("0x606040526002610100"))
|
||||
|
||||
@@ -5,85 +5,96 @@ from manticore.platforms.evm import *
|
||||
from manticore.core.smtlib import *
|
||||
from manticore.core.smtlib.visitors import *
|
||||
from manticore.utils import log
|
||||
#log.set_verbosity(9)
|
||||
|
||||
# log.set_verbosity(9)
|
||||
config.out_of_gas = 1
|
||||
|
||||
def printi(instruction):
|
||||
print(f'Instruction: {instruction}')
|
||||
print(f'\tdescription: {instruction.description}')
|
||||
print(f'\tgroup: {instruction.group}')
|
||||
print(f'\taddress: {instruction.offset}')
|
||||
print(f'\tsize: {instruction.size}')
|
||||
print(f'\thas_operand: {instruction.has_operand}')
|
||||
print(f'\toperand_size: {instruction.operand_size}')
|
||||
print(f'\toperand: {instruction.operand}')
|
||||
print(f'\tsemantics: {instruction.semantics}')
|
||||
print(f'\tpops: {instruction.pops}')
|
||||
print(f'\tpushes:', instruction.pushes)
|
||||
print(f'\tbytes: 0x{instruction.bytes.hex()}')
|
||||
print(f'\twrites to stack: {instruction.writes_to_stack}')
|
||||
print(f'\treads from stack: {instruction.reads_from_stack}')
|
||||
print(f'\twrites to memory: {instruction.writes_to_memory}')
|
||||
print(f'\treads from memory: {instruction.reads_from_memory}')
|
||||
print(f'\twrites to storage: {instruction.writes_to_storage}')
|
||||
print(f'\treads from storage: {instruction.reads_from_storage}')
|
||||
print(f'\tis terminator {instruction.is_terminator}')
|
||||
|
||||
def printi(instruction):
|
||||
print(f"Instruction: {instruction}")
|
||||
print(f"\tdescription: {instruction.description}")
|
||||
print(f"\tgroup: {instruction.group}")
|
||||
print(f"\taddress: {instruction.offset}")
|
||||
print(f"\tsize: {instruction.size}")
|
||||
print(f"\thas_operand: {instruction.has_operand}")
|
||||
print(f"\toperand_size: {instruction.operand_size}")
|
||||
print(f"\toperand: {instruction.operand}")
|
||||
print(f"\tsemantics: {instruction.semantics}")
|
||||
print(f"\tpops: {instruction.pops}")
|
||||
print(f"\tpushes:", instruction.pushes)
|
||||
print(f"\tbytes: 0x{instruction.bytes.hex()}")
|
||||
print(f"\twrites to stack: {instruction.writes_to_stack}")
|
||||
print(f"\treads from stack: {instruction.reads_from_stack}")
|
||||
print(f"\twrites to memory: {instruction.writes_to_memory}")
|
||||
print(f"\treads from memory: {instruction.reads_from_memory}")
|
||||
print(f"\twrites to storage: {instruction.writes_to_storage}")
|
||||
print(f"\treads from storage: {instruction.reads_from_storage}")
|
||||
print(f"\tis terminator {instruction.is_terminator}")
|
||||
|
||||
|
||||
constraints = ConstraintSet()
|
||||
|
||||
|
||||
code = EVMAsm.assemble(
|
||||
'''
|
||||
"""
|
||||
MSTORE
|
||||
'''
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
data = constraints.new_array(index_bits=256, name='array')
|
||||
data = constraints.new_array(index_bits=256, name="array")
|
||||
|
||||
class callbacks():
|
||||
|
||||
class callbacks:
|
||||
initial_stack = []
|
||||
|
||||
def will_execute_instruction(self, pc, instr):
|
||||
for i in range(len(evm.stack), instr.pops):
|
||||
e = constraints.new_bitvec(256, name=f'stack_{len(self.initial_stack)}')
|
||||
e = constraints.new_bitvec(256, name=f"stack_{len(self.initial_stack)}")
|
||||
self.initial_stack.append(e)
|
||||
evm.stack.insert(0, e)
|
||||
|
||||
class DummyWorld():
|
||||
|
||||
class DummyWorld:
|
||||
def __init__(self, constraints):
|
||||
self.balances = constraints.new_array(index_bits=256, value_bits=256, name='balances')
|
||||
self.storage = constraints.new_array(index_bits=256, value_bits=256, name='storage')
|
||||
self.origin = constraints.new_bitvec(256, name='origin')
|
||||
self.price = constraints.new_bitvec(256, name='price')
|
||||
self.timestamp = constraints.new_bitvec(256, name='timestamp')
|
||||
self.coinbase = constraints.new_bitvec(256, name='coinbase')
|
||||
self.gaslimit = constraints.new_bitvec(256, name='gaslimit')
|
||||
self.difficulty = constraints.new_bitvec(256, name='difficulty')
|
||||
self.number = constraints.new_bitvec(256, name='number')
|
||||
self.balances = constraints.new_array(index_bits=256, value_bits=256, name="balances")
|
||||
self.storage = constraints.new_array(index_bits=256, value_bits=256, name="storage")
|
||||
self.origin = constraints.new_bitvec(256, name="origin")
|
||||
self.price = constraints.new_bitvec(256, name="price")
|
||||
self.timestamp = constraints.new_bitvec(256, name="timestamp")
|
||||
self.coinbase = constraints.new_bitvec(256, name="coinbase")
|
||||
self.gaslimit = constraints.new_bitvec(256, name="gaslimit")
|
||||
self.difficulty = constraints.new_bitvec(256, name="difficulty")
|
||||
self.number = constraints.new_bitvec(256, name="number")
|
||||
|
||||
def get_balance(self, address):
|
||||
return self.balances[address]
|
||||
|
||||
def tx_origin(self):
|
||||
return self.origin
|
||||
|
||||
def tx_gasprice(self):
|
||||
return self.price
|
||||
|
||||
def block_coinbase(self):
|
||||
return self.coinbase
|
||||
|
||||
def block_timestamp(self):
|
||||
return self.timestamp
|
||||
|
||||
def block_number(self):
|
||||
return self.number
|
||||
|
||||
def block_difficulty(self):
|
||||
return self.difficulty
|
||||
|
||||
def block_gaslimit(self):
|
||||
return self.gaslimit
|
||||
|
||||
def get_storage_data(self, address, offset):
|
||||
#This works on a single account address
|
||||
# This works on a single account address
|
||||
return self.storage[offset]
|
||||
|
||||
|
||||
def set_storage_data(self, address, offset, value):
|
||||
self.storage[offset] = value
|
||||
|
||||
@@ -97,26 +108,26 @@ class DummyWorld():
|
||||
self.balances[recipient] = dest
|
||||
|
||||
|
||||
caller = constraints.new_bitvec(256, name='caller')
|
||||
value = constraints.new_bitvec(256, name='value')
|
||||
caller = constraints.new_bitvec(256, name="caller")
|
||||
value = constraints.new_bitvec(256, name="value")
|
||||
|
||||
world = DummyWorld(constraints)
|
||||
callbacks = callbacks()
|
||||
|
||||
#evm = world.current_vm
|
||||
# evm = world.current_vm
|
||||
evm = EVM(constraints, 0x41424344454647484950, data, caller, value, code, world=world, gas=1000000)
|
||||
evm.subscribe('will_execute_instruction', callbacks.will_execute_instruction)
|
||||
evm.subscribe("will_execute_instruction", callbacks.will_execute_instruction)
|
||||
|
||||
print("CODE:")
|
||||
while not issymbolic(evm.pc):
|
||||
print(f'\t {evm.pc} {evm.instruction}')
|
||||
print(f"\t {evm.pc} {evm.instruction}")
|
||||
try:
|
||||
evm.execute()
|
||||
except EndTx as e:
|
||||
print(type(e))
|
||||
break
|
||||
|
||||
#print translate_to_smtlib(arithmetic_simplifier(evm.stack[0]))
|
||||
# print translate_to_smtlib(arithmetic_simplifier(evm.stack[0]))
|
||||
print(f"STORAGE = {translate_to_smtlib(world.storage)}")
|
||||
print(f"MEM = {translate_to_smtlib(evm.memory)}")
|
||||
|
||||
@@ -126,5 +137,6 @@ for i in range(len(callbacks.initial_stack)):
|
||||
print("CONSTRAINTS:")
|
||||
print(constraints)
|
||||
|
||||
print(f"PC: {translate_to_smtlib(evm.pc)} {solver.get_all_values(constraints, evm.pc, maxcnt=3, silent=True)}")
|
||||
|
||||
print(
|
||||
f"PC: {translate_to_smtlib(evm.pc)} {solver.get_all_values(constraints, evm.pc, maxcnt=3, silent=True)}"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ from manticore.ethereum import ManticoreEVM
|
||||
|
||||
m = ManticoreEVM()
|
||||
# And now make the contract account to analyze
|
||||
source_code = '''
|
||||
source_code = """
|
||||
contract C {
|
||||
uint n;
|
||||
function C(uint x) {
|
||||
@@ -19,7 +19,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
"""
|
||||
|
||||
user_account = m.create_account(balance=1000)
|
||||
print("[+] Creating a user account", user_account)
|
||||
@@ -31,12 +31,10 @@ print(source_code)
|
||||
|
||||
print("[+] Now the symbolic values")
|
||||
symbolic_data = m.make_symbolic_buffer(320)
|
||||
symbolic_value = m.make_symbolic_value(name='value')
|
||||
m.transaction(caller=user_account,
|
||||
address=contract_account,
|
||||
value=symbolic_value,
|
||||
data=symbolic_data,
|
||||
)
|
||||
symbolic_value = m.make_symbolic_value(name="value")
|
||||
m.transaction(
|
||||
caller=user_account, address=contract_account, value=symbolic_value, data=symbolic_data
|
||||
)
|
||||
|
||||
print("[+] Resulting balances are:")
|
||||
for state in m.running_states:
|
||||
|
||||
+14
-14
@@ -3,30 +3,30 @@ from manticore.ethereum import ManticoreEVM
|
||||
m = ManticoreEVM()
|
||||
m.verbosity(3)
|
||||
# And now make the contract account to analyze
|
||||
with open('coverage.sol') as f:
|
||||
with open("coverage.sol") as f:
|
||||
source_code = f.read()
|
||||
|
||||
user_account = m.create_account(balance=1000)
|
||||
|
||||
bytecode = m.compile(source_code)
|
||||
# Initialize contract
|
||||
contract_account = m.create_contract(owner=user_account,
|
||||
balance=0,
|
||||
init=bytecode)
|
||||
contract_account = m.create_contract(owner=user_account, balance=0, init=bytecode)
|
||||
|
||||
m.transaction(caller=user_account,
|
||||
address=contract_account,
|
||||
value=m.make_symbolic_value(),
|
||||
data=m.make_symbolic_buffer(164),
|
||||
)
|
||||
m.transaction(
|
||||
caller=user_account,
|
||||
address=contract_account,
|
||||
value=m.make_symbolic_value(),
|
||||
data=m.make_symbolic_buffer(164),
|
||||
)
|
||||
|
||||
# Up to here we get only ~30% coverage.
|
||||
# We need 2 transactions to fully explore the contract
|
||||
m.transaction(caller=user_account,
|
||||
address=contract_account,
|
||||
value=m.make_symbolic_value(),
|
||||
data=m.make_symbolic_buffer(164),
|
||||
)
|
||||
m.transaction(
|
||||
caller=user_account,
|
||||
address=contract_account,
|
||||
value=m.make_symbolic_value(),
|
||||
data=m.make_symbolic_buffer(164),
|
||||
)
|
||||
|
||||
print(f"[+] There are {m.count_terminated_states()} reverted states now")
|
||||
print(f"[+] There are {m.count_running_states()} alive states now")
|
||||
|
||||
@@ -6,7 +6,7 @@ m = ManticoreEVM()
|
||||
m.verbosity(3)
|
||||
# And now make the contract account to analyze
|
||||
# https://capturetheether.com/challenges/math/mapping/
|
||||
source_code = '''
|
||||
source_code = """
|
||||
pragma solidity ^0.4.21;
|
||||
|
||||
contract MappingChallenge {
|
||||
@@ -22,21 +22,25 @@ contract MappingChallenge {
|
||||
map[key] = value;
|
||||
}
|
||||
}
|
||||
'''
|
||||
"""
|
||||
print("Source code:\n", source_code)
|
||||
|
||||
|
||||
class StopAtDepth(Detector):
|
||||
''' This just aborts explorations that are too deep '''
|
||||
""" This just aborts explorations that are too deep """
|
||||
|
||||
def will_start_run_callback(self, *args):
|
||||
with self.manticore.locked_context('seen_rep', dict) as reps:
|
||||
with self.manticore.locked_context("seen_rep", dict) as reps:
|
||||
reps.clear()
|
||||
|
||||
def will_decode_instruction_callback(self, state, pc):
|
||||
world = state.platform
|
||||
with self.manticore.locked_context('seen_rep', dict) as reps:
|
||||
item = (world.current_transaction.sort == 'CREATE', world.current_transaction.address, pc)
|
||||
with self.manticore.locked_context("seen_rep", dict) as reps:
|
||||
item = (
|
||||
world.current_transaction.sort == "CREATE",
|
||||
world.current_transaction.address,
|
||||
pc,
|
||||
)
|
||||
if not item in reps:
|
||||
reps[item] = 0
|
||||
reps[item] += 1
|
||||
@@ -60,4 +64,4 @@ for st in m.all_states:
|
||||
if st.can_be_true(flag_value != 0):
|
||||
print("Flag Found! Check ", m.workspace)
|
||||
st.constraints.add(flag_value != 0)
|
||||
m.generate_testcase(st, 'Flag Found', '')
|
||||
m.generate_testcase(st, "Flag Found", "")
|
||||
|
||||
@@ -4,8 +4,8 @@ from manticore.ethereum import ManticoreEVM
|
||||
|
||||
m = ManticoreEVM()
|
||||
|
||||
#And now make the contract account to analyze
|
||||
truffle_json = r'''{
|
||||
# And now make the contract account to analyze
|
||||
truffle_json = r"""{
|
||||
"contractName": "MetaCoin",
|
||||
"abi": [
|
||||
{
|
||||
@@ -1042,25 +1042,24 @@ truffle_json = r'''{
|
||||
"updatedAt": "2017-05-15T20:46:00Z",
|
||||
"schemaVersion": "0.0.5"
|
||||
}
|
||||
'''
|
||||
"""
|
||||
|
||||
user_account = m.create_account(balance=1000, name='user_account')
|
||||
user_account = m.create_account(balance=1000, name="user_account")
|
||||
print("[+] Creating a user account", user_account.name_)
|
||||
|
||||
contract_account = m.json_create_contract(truffle_json, owner=user_account, name='contract_account')
|
||||
contract_account = m.json_create_contract(truffle_json, owner=user_account, name="contract_account")
|
||||
print("[+] Creating a contract account", contract_account.name_)
|
||||
contract_account.sendCoin(1,1)
|
||||
contract_account.sendCoin(1, 1)
|
||||
|
||||
print("[+] Now the symbolic values")
|
||||
symbolic_data = m.make_symbolic_buffer(320)
|
||||
symbolic_data = m.make_symbolic_buffer(320)
|
||||
symbolic_value = m.make_symbolic_value(name="VALUE")
|
||||
symbolic_address = m.make_symbolic_value(name="ADDRESS")
|
||||
symbolic_caller = m.make_symbolic_value(name="CALLER")
|
||||
m.transaction(caller=symbolic_caller,
|
||||
address=symbolic_address,
|
||||
data=symbolic_data,
|
||||
value=symbolic_value )
|
||||
m.transaction(
|
||||
caller=symbolic_caller, address=symbolic_address, data=symbolic_data, value=symbolic_value
|
||||
)
|
||||
|
||||
#Let seth know we are not sending more transactions
|
||||
# Let seth know we are not sending more transactions
|
||||
m.finalize()
|
||||
print(f"[+] Look for results in {m.workspace}")
|
||||
|
||||
+13
-12
@@ -4,9 +4,9 @@ from manticore.ethereum import ManticoreEVM
|
||||
|
||||
m = ManticoreEVM()
|
||||
|
||||
#And now make the contract account to analyze
|
||||
# cat | solc --bin
|
||||
source_code = '''
|
||||
# And now make the contract account to analyze
|
||||
# cat | solc --bin
|
||||
source_code = """
|
||||
contract NoDistpatcher {
|
||||
event Log(string);
|
||||
|
||||
@@ -23,25 +23,26 @@ contract NoDistpatcher {
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
"""
|
||||
|
||||
user_account = m.create_account(balance=1000, name='user_account')
|
||||
user_account = m.create_account(balance=1000, name="user_account")
|
||||
print("[+] Creating a user account", user_account.name_)
|
||||
|
||||
contract_account = m.solidity_create_contract(source_code, owner=user_account, name='contract_account')
|
||||
contract_account = m.solidity_create_contract(
|
||||
source_code, owner=user_account, name="contract_account"
|
||||
)
|
||||
print("[+] Creating a contract account", contract_account.name_)
|
||||
contract_account.named_func(1)
|
||||
|
||||
print("[+] Now the symbolic values")
|
||||
symbolic_data = m.make_symbolic_buffer(320)
|
||||
symbolic_data = m.make_symbolic_buffer(320)
|
||||
symbolic_value = m.make_symbolic_value(name="VALUE")
|
||||
symbolic_address = m.make_symbolic_value(name="ADDRESS")
|
||||
symbolic_caller = m.make_symbolic_value(name="CALLER")
|
||||
m.transaction(caller=symbolic_caller,
|
||||
address=symbolic_address,
|
||||
data=symbolic_data,
|
||||
value=symbolic_value )
|
||||
m.transaction(
|
||||
caller=symbolic_caller, address=symbolic_address, data=symbolic_data, value=symbolic_value
|
||||
)
|
||||
|
||||
#Let seth know we are not sending more transactions
|
||||
# Let seth know we are not sending more transactions
|
||||
m.finalize()
|
||||
print(f"[+] Look for results in {m.workspace}")
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
from manticore.ethereum import evm, ManticoreEVM
|
||||
from binascii import unhexlify, hexlify
|
||||
|
||||
################ Script #######################
|
||||
# Bytecode only based analysis
|
||||
# No solidity, no compiler, no metadata
|
||||
|
||||
m = ManticoreEVM()
|
||||
init_bytecode = unhexlify(b"608060405234801561001057600080fd5b506101cc806100206000396000f30060806040527f41000000000000000000000000000000000000000000000000000000000000006000366000818110151561003557fe5b905001357f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415610135577fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab6040518080602001828103825260088152602001807f476f7420616e204100000000000000000000000000000000000000000000000081525060200191505060405180910390a161019e565b7fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab6040518080602001828103825260128152602001807f476f7420736f6d657468696e6720656c7365000000000000000000000000000081525060200191505060405180910390a15b0000a165627a7a72305820fd5ec850d8409e19cfe593b9ee3276cc3ac12b0e3406d965317dc9c1aeb7f2670029")
|
||||
init_bytecode = unhexlify(
|
||||
b"608060405234801561001057600080fd5b506101cc806100206000396000f30060806040527f41000000000000000000000000000000000000000000000000000000000000006000366000818110151561003557fe5b905001357f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415610135577fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab6040518080602001828103825260088152602001807f476f7420616e204100000000000000000000000000000000000000000000000081525060200191505060405180910390a161019e565b7fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab6040518080602001828103825260128152602001807f476f7420736f6d657468696e6720656c7365000000000000000000000000000081525060200191505060405180910390a15b0000a165627a7a72305820fd5ec850d8409e19cfe593b9ee3276cc3ac12b0e3406d965317dc9c1aeb7f2670029"
|
||||
)
|
||||
|
||||
user_account = m.create_account(balance=1000)
|
||||
print("[+] Creating a user account", user_account)
|
||||
@@ -19,13 +22,12 @@ contract_account = m.create_contract(owner=user_account, init=init_bytecode)
|
||||
print("[+] Creating a contract account", contract_account)
|
||||
|
||||
print("[+] Now the symbolic values")
|
||||
symbolic_data = m.make_symbolic_buffer(320)
|
||||
symbolic_data = m.make_symbolic_buffer(320)
|
||||
symbolic_value = m.make_symbolic_value()
|
||||
m.transaction(caller=user_account,
|
||||
address=contract_account,
|
||||
data=symbolic_data,
|
||||
value=symbolic_value )
|
||||
m.transaction(
|
||||
caller=user_account, address=contract_account, data=symbolic_data, value=symbolic_value
|
||||
)
|
||||
|
||||
#Let seth know we are not sending more transactions
|
||||
# Let seth know we are not sending more transactions
|
||||
m.finalize()
|
||||
print(f"[+] Look for results in {m.workspace}")
|
||||
|
||||
@@ -4,8 +4,8 @@ from manticore.ethereum import ABI, ManticoreEVM
|
||||
|
||||
m = ManticoreEVM()
|
||||
m.verbosity(0)
|
||||
#The contract account to analyze
|
||||
contract_source_code = '''
|
||||
# The contract account to analyze
|
||||
contract_source_code = """
|
||||
pragma solidity ^0.4.15;
|
||||
|
||||
contract Reentrance {
|
||||
@@ -32,9 +32,9 @@ contract Reentrance {
|
||||
//c0e317fb: addToBalance()
|
||||
//f8b2cb4f: getBalance(address)
|
||||
//5fd8c710: withdrawBalance()
|
||||
'''
|
||||
"""
|
||||
|
||||
exploit_source_code = '''
|
||||
exploit_source_code = """
|
||||
pragma solidity ^0.4.15;
|
||||
|
||||
contract GenericReentranceExploit {
|
||||
@@ -77,15 +77,17 @@ contract GenericReentranceExploit {
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
#Initialize user and contracts
|
||||
# Initialize user and contracts
|
||||
user_account = m.create_account(balance=100000000000000000)
|
||||
attacker_account = m.create_account(balance=100000000000000000)
|
||||
|
||||
contract_account = m.solidity_create_contract(contract_source_code, owner=user_account) #Not payable
|
||||
m.world.set_balance(contract_account, 1000000000000000000) #give it some ether
|
||||
contract_account = m.solidity_create_contract(
|
||||
contract_source_code, owner=user_account
|
||||
) # Not payable
|
||||
m.world.set_balance(contract_account, 1000000000000000000) # give it some ether
|
||||
|
||||
exploit_account = m.solidity_create_contract(exploit_source_code, owner=attacker_account)
|
||||
|
||||
@@ -95,34 +97,43 @@ exploit_account.set_reentry_reps(2)
|
||||
|
||||
print("[+] Setting attack string")
|
||||
#'\x9d\x15\xfd\x17'+pack_msb(32)+pack_msb(4)+'\x5f\xd8\xc7\x10',
|
||||
reentry_string = ABI.function_selector('withdrawBalance()')
|
||||
reentry_string = ABI.function_selector("withdrawBalance()")
|
||||
exploit_account.set_reentry_attack_string(reentry_string)
|
||||
|
||||
print("[+] Initial world state")
|
||||
print(f" attacker_account {attacker_account.address:x} balance: {m.get_balance(attacker_account.address)}")
|
||||
print(f" exploit_account {exploit_account.address} balance: {m.get_balance(exploit_account.address)}")
|
||||
print(
|
||||
f" attacker_account {attacker_account.address:x} balance: {m.get_balance(attacker_account.address)}"
|
||||
)
|
||||
print(
|
||||
f" exploit_account {exploit_account.address} balance: {m.get_balance(exploit_account.address)}"
|
||||
)
|
||||
print(f" user_account {user_account.address:x} balance: {m.get_balance(user_account.address)}")
|
||||
print(f" contract_account {contract_account.address:x} balance: {m.get_balance(contract_account.address)}")
|
||||
print(
|
||||
f" contract_account {contract_account.address:x} balance: {m.get_balance(contract_account.address)}"
|
||||
)
|
||||
|
||||
|
||||
#User deposits all in contract
|
||||
# User deposits all in contract
|
||||
print("[+] user deposited some.")
|
||||
contract_account.addToBalance(value=100000000000000000)
|
||||
|
||||
|
||||
print("[+] Let attacker deposit some small amount using exploit")
|
||||
exploit_account.proxycall(ABI.function_selector('addToBalance()'), value=100000000000000000)
|
||||
exploit_account.proxycall(ABI.function_selector("addToBalance()"), value=100000000000000000)
|
||||
|
||||
print("[+] Let attacker extract all using exploit")
|
||||
exploit_account.proxycall(ABI.function_selector('withdrawBalance()'))
|
||||
print("[+] Let attacker extract all using exploit")
|
||||
exploit_account.proxycall(ABI.function_selector("withdrawBalance()"))
|
||||
|
||||
print("[+] Let attacker destroy the exploit contract and profit")
|
||||
exploit_account.get_money()
|
||||
print("[+] Let attacker destroy the exploit contract and profit")
|
||||
exploit_account.get_money()
|
||||
|
||||
print(f" attacker_account {attacker_account.address:x} balance: {m.get_balance(attacker_account.address)}")
|
||||
print(
|
||||
f" attacker_account {attacker_account.address:x} balance: {m.get_balance(attacker_account.address)}"
|
||||
)
|
||||
print(f" user_account {user_account.address:x} balance: {m.get_balance(user_account.address)}")
|
||||
print(f" contract_account {contract_account.address:x} balance: {m.get_balance(contract_account.address)}")
|
||||
print(
|
||||
f" contract_account {contract_account.address:x} balance: {m.get_balance(contract_account.address)}"
|
||||
)
|
||||
|
||||
m.finalize()
|
||||
print(f"[+] Look for results in {m.workspace}")
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ from manticore.ethereum import ManticoreEVM
|
||||
|
||||
m = ManticoreEVM()
|
||||
m.verbosity(0)
|
||||
#The contract account to analyze
|
||||
contract_source_code = '''
|
||||
# The contract account to analyze
|
||||
contract_source_code = """
|
||||
pragma solidity ^0.4.15;
|
||||
|
||||
contract Reentrance {
|
||||
@@ -28,9 +28,9 @@ contract Reentrance {
|
||||
userBalance[msg.sender] = 0;
|
||||
}
|
||||
}
|
||||
'''
|
||||
"""
|
||||
|
||||
exploit_source_code = '''
|
||||
exploit_source_code = """
|
||||
pragma solidity ^0.4.15;
|
||||
|
||||
contract GenericReentranceExploit {
|
||||
@@ -73,25 +73,33 @@ contract GenericReentranceExploit {
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
#Initialize user and contracts
|
||||
# Initialize user and contracts
|
||||
user_account = m.create_account(balance=100000000000000000)
|
||||
attacker_account = m.create_account(balance=100000000000000000)
|
||||
contract_account = m.solidity_create_contract(contract_source_code, owner=user_account) #Not payable
|
||||
contract_account = m.solidity_create_contract(
|
||||
contract_source_code, owner=user_account
|
||||
) # Not payable
|
||||
exploit_account = m.solidity_create_contract(exploit_source_code, owner=attacker_account)
|
||||
|
||||
|
||||
#User deposits all in contract
|
||||
# User deposits all in contract
|
||||
print("[+] user deposited some.")
|
||||
contract_account.addToBalance(value=100000000000000000)
|
||||
|
||||
print("[+] Initial world state")
|
||||
print(f" attacker_account {attacker_account.address:x} balance: {m.get_balance(attacker_account.address)}")
|
||||
print(f" exploit_account {exploit_account.address:x} balance: {m.get_balance(exploit_account.address)}")
|
||||
print(
|
||||
f" attacker_account {attacker_account.address:x} balance: {m.get_balance(attacker_account.address)}"
|
||||
)
|
||||
print(
|
||||
f" exploit_account {exploit_account.address:x} balance: {m.get_balance(exploit_account.address)}"
|
||||
)
|
||||
print(f" user_account {user_account.address:x} balance: {m.get_balance(user_account.address)}")
|
||||
print(f" contract_account {contract_account.address} balance: {m.get_balance(contract_account.address)}")
|
||||
print(
|
||||
f" contract_account {contract_account.address} balance: {m.get_balance(contract_account.address)}"
|
||||
)
|
||||
|
||||
|
||||
print("[+] Set up the exploit")
|
||||
@@ -103,18 +111,17 @@ exploit_account.set_reentry_reps(30)
|
||||
print("\t Setting reply string")
|
||||
exploit_account.set_reentry_attack_string(m.make_symbolic_buffer(4))
|
||||
|
||||
#Attacker is
|
||||
# Attacker is
|
||||
print("[+] Attacker first transaction")
|
||||
exploit_account.proxycall(m.make_symbolic_buffer(4), value=m.make_symbolic_value())
|
||||
|
||||
print("[+] Attacker second transaction")
|
||||
print("[+] Attacker second transaction")
|
||||
exploit_account.proxycall(m.make_symbolic_buffer(4))
|
||||
|
||||
print("[+] The attacker destroys the exploit contract and profit")
|
||||
print("[+] The attacker destroys the exploit contract and profit")
|
||||
exploit_account.get_money()
|
||||
|
||||
#Let seth know we are not sending more transactions so it can output
|
||||
# Let seth know we are not sending more transactions so it can output
|
||||
# info about running states and global statistics
|
||||
m.finalize()
|
||||
print(f"[+] Look for results in {m.workspace}")
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ from manticore.ethereum import ManticoreEVM
|
||||
m = ManticoreEVM()
|
||||
m.verbosity(2)
|
||||
# And now make the contract account to analyze
|
||||
# cat | solc --bin
|
||||
source_code = '''
|
||||
# cat | solc --bin
|
||||
source_code = """
|
||||
pragma solidity ^0.4.13;
|
||||
|
||||
contract Test {
|
||||
@@ -27,18 +27,16 @@ contract Test {
|
||||
}
|
||||
|
||||
}
|
||||
'''
|
||||
"""
|
||||
# Initialize accounts
|
||||
user_account = m.create_account(balance=1000)
|
||||
contract_account = m.solidity_create_contract(source_code, owner=user_account)
|
||||
|
||||
symbolic_data = m.make_symbolic_buffer(64)
|
||||
symbolic_value = 0
|
||||
m.transaction(caller=user_account,
|
||||
address=contract_account,
|
||||
value=symbolic_value,
|
||||
data=symbolic_data
|
||||
)
|
||||
m.transaction(
|
||||
caller=user_account, address=contract_account, value=symbolic_value, data=symbolic_data
|
||||
)
|
||||
|
||||
m.finalize()
|
||||
print(f"[+] Look for results in {m.workspace}")
|
||||
|
||||
+14
-16
@@ -7,8 +7,8 @@ from manticore.ethereum import ManticoreEVM
|
||||
m = ManticoreEVM()
|
||||
m.verbosity(0)
|
||||
# And now make the contract account to analyze
|
||||
# cat | solc --bin
|
||||
source_code = '''
|
||||
# cat | solc --bin
|
||||
source_code = """
|
||||
pragma solidity ^0.4;
|
||||
contract C {
|
||||
uint c;
|
||||
@@ -35,17 +35,17 @@ contract C {
|
||||
|
||||
}
|
||||
}
|
||||
'''
|
||||
"""
|
||||
print(source_code)
|
||||
|
||||
|
||||
class EVMUseDef(Plugin):
|
||||
def _get_concrete_hex(self, state, array):
|
||||
r = ''
|
||||
r = ""
|
||||
for i in array:
|
||||
l = state.solve_n(i, 2)
|
||||
if len(l) == 1:
|
||||
r += '%02x' % l[0]
|
||||
r += "%02x" % l[0]
|
||||
if len(r) != 8:
|
||||
return
|
||||
return r
|
||||
@@ -61,7 +61,7 @@ class EVMUseDef(Plugin):
|
||||
return
|
||||
|
||||
offsets = state.solve_n(offset, 3000)
|
||||
with self.locked_context('storage_writes', dict) as storage_writes:
|
||||
with self.locked_context("storage_writes", dict) as storage_writes:
|
||||
contract_function = (md.name, md.get_func_name(r))
|
||||
if contract_function not in storage_writes:
|
||||
storage_writes[contract_function] = set()
|
||||
@@ -79,7 +79,7 @@ class EVMUseDef(Plugin):
|
||||
return
|
||||
|
||||
offsets = state.solve_n(offset, 3000)
|
||||
with self.locked_context('storage_reads', dict) as storage_reads:
|
||||
with self.locked_context("storage_reads", dict) as storage_reads:
|
||||
contract_function = (md.name, md.get_func_name(r))
|
||||
if contract_function not in storage_reads:
|
||||
storage_reads[contract_function] = set()
|
||||
@@ -95,15 +95,13 @@ m.register_plugin(p)
|
||||
|
||||
symbolic_data = m.make_symbolic_buffer(320)
|
||||
symbolic_value = m.make_symbolic_value()
|
||||
m.transaction(caller=user_account,
|
||||
address=contract_account,
|
||||
value=symbolic_value,
|
||||
data=symbolic_data
|
||||
)
|
||||
print('READS', p.context['storage_reads'])
|
||||
print('WRITES', p.context['storage_writes'])
|
||||
m.transaction(
|
||||
caller=user_account, address=contract_account, value=symbolic_value, data=symbolic_data
|
||||
)
|
||||
print("READS", p.context["storage_reads"])
|
||||
print("WRITES", p.context["storage_writes"])
|
||||
|
||||
print('It makes no sense to try f3() after 1 tx')
|
||||
print("It makes no sense to try f3() after 1 tx")
|
||||
|
||||
m.finalize()
|
||||
print(f'[+] Look for results in {m.workspace}')
|
||||
print(f"[+] Look for results in {m.workspace}")
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from manticore import Manticore
|
||||
|
||||
|
||||
def fixme():
|
||||
raise Exception("Fill in the blanks!")
|
||||
raise Exception("Fill in the blanks!")
|
||||
|
||||
|
||||
# Let's initialize the manticore control object
|
||||
m = Manticore('multiple-styles')
|
||||
m = Manticore("multiple-styles")
|
||||
|
||||
# First, let's give it some fake data for the input. Anything the same size as
|
||||
# the real flag should work fine!
|
||||
@@ -12,7 +14,7 @@ m.concrete_data = "infiltrate miami!"
|
||||
|
||||
# Now we're going to want to execute a few different hooks and share data, so
|
||||
# let's use the m.context dict to keep our solution in
|
||||
m.context['solution'] = ''
|
||||
m.context["solution"] = ""
|
||||
|
||||
# Now we want to hook that compare instruction that controls the main loop.
|
||||
# Where is it again?
|
||||
@@ -22,14 +24,15 @@ def solve(state):
|
||||
# just read it out real quick
|
||||
flag_byte = state.cpu.AL - fixme()
|
||||
|
||||
m.context['solution'] += chr(flag_byte)
|
||||
m.context["solution"] += chr(flag_byte)
|
||||
|
||||
# But how can we make the comparison pass? There are a couple solutions here
|
||||
fixme()
|
||||
|
||||
|
||||
# play with these numbers!
|
||||
m.verbosity = 0
|
||||
procs = 1
|
||||
|
||||
m.run(procs)
|
||||
print(m.context['solution'])
|
||||
print(m.context["solution"])
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from manticore import Manticore
|
||||
|
||||
|
||||
def fixme():
|
||||
raise Exception("Fill in the blanks!")
|
||||
raise Exception("Fill in the blanks!")
|
||||
|
||||
|
||||
# Let's initialize the manticore control object
|
||||
m = Manticore('multiple-styles')
|
||||
m = Manticore("multiple-styles")
|
||||
|
||||
# Now, we can hook the success state and figure out the flag! `fixme()` here
|
||||
# should be an address we'd like to get to
|
||||
@@ -15,7 +17,7 @@ def solve(state):
|
||||
flag_base = state.cpu.RBP - fixme()
|
||||
|
||||
# We're going to build a solution later
|
||||
solution = ''
|
||||
solution = ""
|
||||
|
||||
# How big is the flag? We should be able to figure this out from traditional
|
||||
# static analysis
|
||||
@@ -31,6 +33,7 @@ def solve(state):
|
||||
print(solution)
|
||||
m.terminate()
|
||||
|
||||
|
||||
# play with these numbers!
|
||||
m.verbosity = 0
|
||||
procs = 1
|
||||
|
||||
@@ -135,16 +135,11 @@ def func(password, pad, flag=True):
|
||||
SUBPROGRAMFALSE = """ printf("You are NOT in!\\n");\n"""
|
||||
else:
|
||||
SUBPROGRAMFALSE = func(
|
||||
"".join(random.choice(chars) for _ in range(len(password) // 2)),
|
||||
pad[1:],
|
||||
False,
|
||||
"".join(random.choice(chars) for _ in range(len(password) // 2)), pad[1:], False
|
||||
)
|
||||
|
||||
config = random.choice(
|
||||
[
|
||||
(True, SUBPROGRAMTRUE, SUBPROGRAMFALSE),
|
||||
(False, SUBPROGRAMFALSE, SUBPROGRAMTRUE),
|
||||
]
|
||||
[(True, SUBPROGRAMTRUE, SUBPROGRAMFALSE), (False, SUBPROGRAMFALSE, SUBPROGRAMTRUE)]
|
||||
)
|
||||
|
||||
SUBPROGRAM = ""
|
||||
|
||||
@@ -13,31 +13,31 @@ from manticore.native import Manticore
|
||||
# printf "ffffff" | PYTHONPATH=. ./examples/script/aarch64/basic.py
|
||||
|
||||
DIR = os.path.dirname(__file__)
|
||||
FILE = os.path.join(DIR, 'basic')
|
||||
FILE = os.path.join(DIR, "basic")
|
||||
STDIN = sys.stdin.readline()
|
||||
|
||||
# Avoid writing anything to 'STDIN' here. Do it in the 'init' hook as that's
|
||||
# more flexible.
|
||||
m = Manticore(FILE, concrete_start='', stdin_size=0)
|
||||
m = Manticore(FILE, concrete_start="", stdin_size=0)
|
||||
|
||||
|
||||
@m.init
|
||||
def init(m, ready_states):
|
||||
for state in ready_states:
|
||||
state.platform.input.write(state.symbolicate_buffer(STDIN, label='STDIN'))
|
||||
state.platform.input.write(state.symbolicate_buffer(STDIN, label="STDIN"))
|
||||
|
||||
|
||||
# Hook the 'if' case.
|
||||
@m.hook(0x4006bc)
|
||||
@m.hook(0x4006BC)
|
||||
def hook_if(state):
|
||||
print('hook if')
|
||||
print("hook if")
|
||||
state.abandon()
|
||||
|
||||
|
||||
# Hook the 'else' case.
|
||||
@m.hook(0x4006cc)
|
||||
@m.hook(0x4006CC)
|
||||
def hook_else(state):
|
||||
print('hook else')
|
||||
print("hook else")
|
||||
# See how the constraints are affected by input.
|
||||
print_constraints(state, 6)
|
||||
|
||||
@@ -46,23 +46,23 @@ def hook_else(state):
|
||||
if isinstance(w0, int): # concrete
|
||||
print(hex(w0))
|
||||
else:
|
||||
print(w0) # symbolic
|
||||
print(w0) # symbolic
|
||||
|
||||
solved = state.solve_one(w0)
|
||||
print(struct.pack("<I", solved))
|
||||
|
||||
|
||||
# Hook 'puts' in the 'else' case.
|
||||
@m.hook(0x4006d4)
|
||||
@m.hook(0x4006D4)
|
||||
def hook_puts(state):
|
||||
print('hook puts')
|
||||
print("hook puts")
|
||||
cpu = state.cpu
|
||||
print(cpu.read_string(cpu.X0))
|
||||
|
||||
|
||||
def print_constraints(state, nlines):
|
||||
i = 0
|
||||
for c in str(state.constraints).split('\n'):
|
||||
for c in str(state.constraints).split("\n"):
|
||||
if i >= nlines:
|
||||
break
|
||||
print(c)
|
||||
|
||||
@@ -8,42 +8,46 @@ from manticore.native import Manticore
|
||||
# statically-linked "Hello, world!" AArch64 binary.
|
||||
|
||||
DIR = os.path.dirname(__file__)
|
||||
FILE = os.path.join(DIR, 'hello42')
|
||||
FILE = os.path.join(DIR, "hello42")
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
m = Manticore(FILE)
|
||||
|
||||
with m.locked_context() as context:
|
||||
context['count'] = 0
|
||||
context["count"] = 0
|
||||
|
||||
@m.hook(None)
|
||||
def explore(state):
|
||||
with m.locked_context() as context:
|
||||
context['count'] += 1
|
||||
context["count"] += 1
|
||||
|
||||
if state.cpu.PC == 0x406f10: # puts
|
||||
if state.cpu.PC == 0x406F10: # puts
|
||||
s = state.cpu.read_string(state.cpu.X0)
|
||||
assert s == 'hello'
|
||||
print(f'puts argument: {s}')
|
||||
assert s == "hello"
|
||||
print(f"puts argument: {s}")
|
||||
|
||||
elif state.cpu.PC == 0x40706c: # puts result
|
||||
elif state.cpu.PC == 0x40706C: # puts result
|
||||
result = state.cpu.X0
|
||||
assert result >= 0
|
||||
print(f'puts result: {result}')
|
||||
print(f"puts result: {result}")
|
||||
|
||||
elif state.cpu.PC == 0x415e50: # exit
|
||||
elif state.cpu.PC == 0x415E50: # exit
|
||||
status = state.cpu.X0
|
||||
syscall = state.cpu.X8
|
||||
assert syscall == 94 # sys_exit_group
|
||||
print(f'exit status: {status}')
|
||||
print(f"exit status: {status}")
|
||||
|
||||
def execute_instruction(self, insn, msg):
|
||||
print(f'{msg}: 0x{insn.address:x}: {insn.mnemonic} {insn.op_str}')
|
||||
print(f"{msg}: 0x{insn.address:x}: {insn.mnemonic} {insn.op_str}")
|
||||
|
||||
m.subscribe('will_execute_instruction', lambda self, state, pc, insn:
|
||||
execute_instruction(self, insn, 'next'))
|
||||
m.subscribe('did_execute_instruction', lambda self, state, last_pc, pc, insn:
|
||||
execute_instruction(self, insn, 'done'))
|
||||
m.subscribe(
|
||||
"will_execute_instruction",
|
||||
lambda self, state, pc, insn: execute_instruction(self, insn, "next"),
|
||||
)
|
||||
m.subscribe(
|
||||
"did_execute_instruction",
|
||||
lambda self, state, last_pc, pc, insn: execute_instruction(self, insn, "done"),
|
||||
)
|
||||
|
||||
m.run()
|
||||
|
||||
|
||||
+68
-43
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
"""
|
||||
Rough concolic execution implementation
|
||||
|
||||
Limitations
|
||||
@@ -11,7 +11,7 @@ Bugs
|
||||
- Will probably break if a newly discovered branch gets more input/does another read(2)
|
||||
- possibly unnecessary deepcopies
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
import queue
|
||||
import struct
|
||||
@@ -26,15 +26,18 @@ from manticore.utils import config
|
||||
import copy
|
||||
from manticore.core.smtlib.expression import *
|
||||
|
||||
prog = '../linux/simpleassert'
|
||||
prog = "../linux/simpleassert"
|
||||
VERBOSITY = 0
|
||||
|
||||
|
||||
def _partition(pred, iterable):
|
||||
t1, t2 = itertools.tee(iterable)
|
||||
return (list(itertools.filterfalse(pred, t1)), list(filter(pred, t2)))
|
||||
|
||||
|
||||
def log(s):
|
||||
print('[+]', s)
|
||||
print("[+]", s)
|
||||
|
||||
|
||||
class TraceReceiver(Plugin):
|
||||
def __init__(self, tracer):
|
||||
@@ -49,18 +52,21 @@ class TraceReceiver(Plugin):
|
||||
def will_terminate_state_callback(self, state, reason):
|
||||
self._trace = state.context.get(self._tracer.context_key, [])
|
||||
|
||||
instructions, writes = _partition(lambda x: x['type'] == 'regs', self._trace)
|
||||
instructions, writes = _partition(lambda x: x["type"] == "regs", self._trace)
|
||||
total = len(self._trace)
|
||||
log(f'Recorded concrete trace: {len(instructions)}/{total} instructions, {len(writes)}/{total} writes')
|
||||
log(
|
||||
f"Recorded concrete trace: {len(instructions)}/{total} instructions, {len(writes)}/{total} writes"
|
||||
)
|
||||
|
||||
|
||||
def flip(constraint):
|
||||
'''
|
||||
"""
|
||||
flips a constraint (Equal)
|
||||
|
||||
(Equal (BitVecITE Cond IfC ElseC) IfC)
|
||||
->
|
||||
(Equal (BitVecITE Cond IfC ElseC) ElseC)
|
||||
'''
|
||||
"""
|
||||
equal = copy.copy(constraint)
|
||||
|
||||
assert len(equal.operands) == 2
|
||||
@@ -71,10 +77,11 @@ def flip(constraint):
|
||||
cond, iifpc, eelsepc = ite.operands
|
||||
assert isinstance(iifpc, BitVecConstant) and isinstance(eelsepc, BitVecConstant)
|
||||
|
||||
equal._operands= (equal.operands[0], eelsepc if forcepc.value == iifpc.value else iifpc)
|
||||
equal._operands = (equal.operands[0], eelsepc if forcepc.value == iifpc.value else iifpc)
|
||||
|
||||
return equal
|
||||
|
||||
|
||||
def eq(a, b):
|
||||
# this ignores checking the conditions, only checks the 2 possible pcs
|
||||
# the one that it is forced to
|
||||
@@ -95,8 +102,9 @@ def eq(a, b):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def perm(lst, func):
|
||||
''' Produce permutations of `lst`, where permutations are mutated by `func`. Used for flipping constraints. highly
|
||||
""" Produce permutations of `lst`, where permutations are mutated by `func`. Used for flipping constraints. highly
|
||||
possible that returned constraints can be unsat this does it blindly, without any attention to the constraints
|
||||
themselves
|
||||
|
||||
@@ -119,55 +127,60 @@ def perm(lst, func):
|
||||
The code below yields lists of constraints permuted as above by treating list indeces as bitmasks from 1 to
|
||||
2**len(lst) and applying func to all the set bit offsets.
|
||||
|
||||
'''
|
||||
for i in range(1, 2**len(lst)):
|
||||
yield [func(item) if (1<<j)&i else item for (j, item) in enumerate(lst)]
|
||||
"""
|
||||
for i in range(1, 2 ** len(lst)):
|
||||
yield [func(item) if (1 << j) & i else item for (j, item) in enumerate(lst)]
|
||||
|
||||
|
||||
def constraints_to_constraintset(constupl):
|
||||
x = ConstraintSet()
|
||||
x._constraints = list(constupl)
|
||||
return x
|
||||
|
||||
|
||||
def input_from_cons(constupl, datas):
|
||||
' solve bytes in |datas| based on '
|
||||
" solve bytes in |datas| based on "
|
||||
|
||||
def make_chr(c):
|
||||
try:
|
||||
return chr(c)
|
||||
except Exception:
|
||||
return c
|
||||
|
||||
newset = constraints_to_constraintset(constupl)
|
||||
|
||||
ret = ''
|
||||
ret = ""
|
||||
for data in datas:
|
||||
for c in data:
|
||||
ret += make_chr(Z3Solver.instance().get_value(newset, c))
|
||||
return ret
|
||||
|
||||
|
||||
# Run a concrete run with |inp| as stdin
|
||||
def concrete_run_get_trace(inp):
|
||||
|
||||
consts = config.get_group('core')
|
||||
|
||||
consts = config.get_group("core")
|
||||
consts.mprocessing = consts.mprocessing.single
|
||||
|
||||
m1 = Manticore.linux(prog, concrete_start=inp, workspace_url='mem:')
|
||||
m1 = Manticore.linux(prog, concrete_start=inp, workspace_url="mem:")
|
||||
t = ExtendedTracer()
|
||||
#r = TraceReceiver(t)
|
||||
# r = TraceReceiver(t)
|
||||
m1.verbosity(VERBOSITY)
|
||||
m1.register_plugin(t)
|
||||
#m1.register_plugin(r)
|
||||
# m1.register_plugin(r)
|
||||
m1.run()
|
||||
for st in m1.all_states:
|
||||
return t.get_trace(st)
|
||||
#return r.trace
|
||||
return t.get_trace(st)
|
||||
# return r.trace
|
||||
|
||||
|
||||
def symbolic_run_get_cons(trace):
|
||||
'''
|
||||
"""
|
||||
Execute a symbolic run that follows a concrete run; return constraints generated
|
||||
and the stdin data produced
|
||||
'''
|
||||
"""
|
||||
# mem: has no concurrency support. Manticore should be 'Single' process
|
||||
m2 = Manticore.linux(prog, workspace_url='mem:')
|
||||
m2 = Manticore.linux(prog, workspace_url="mem:")
|
||||
f = Follower(trace)
|
||||
m2.verbosity(VERBOSITY)
|
||||
m2.register_plugin(f)
|
||||
@@ -176,32 +189,36 @@ def symbolic_run_get_cons(trace):
|
||||
with m2.locked_context() as ctx:
|
||||
readdata = []
|
||||
for name, fd, data in state.platform.syscall_trace:
|
||||
if name in ('_receive', '_read') and fd == 0:
|
||||
if name in ("_receive", "_read") and fd == 0:
|
||||
readdata.append(data)
|
||||
ctx['readdata'] = readdata
|
||||
ctx['constraints'] = list(state.constraints.constraints)
|
||||
ctx["readdata"] = readdata
|
||||
ctx["constraints"] = list(state.constraints.constraints)
|
||||
|
||||
m2.subscribe('will_terminate_state', on_term_testcase)
|
||||
m2.subscribe("will_terminate_state", on_term_testcase)
|
||||
|
||||
m2.run()
|
||||
|
||||
constraints = m2.context['constraints']
|
||||
datas = m2.context['readdata']
|
||||
constraints = m2.context["constraints"]
|
||||
datas = m2.context["readdata"]
|
||||
|
||||
return constraints, datas
|
||||
|
||||
|
||||
def contains(new, olds):
|
||||
'__contains__ operator using the `eq` function'
|
||||
"__contains__ operator using the `eq` function"
|
||||
return any(eq(new, old) for old in olds)
|
||||
|
||||
|
||||
def getnew(oldcons, newcons):
|
||||
"return all constraints in newcons that aren't in oldcons"
|
||||
return [new for new in newcons if not contains(new, oldcons)]
|
||||
|
||||
|
||||
def constraints_are_sat(cons):
|
||||
'Whether constraints are sat'
|
||||
"Whether constraints are sat"
|
||||
return Z3Solver.instance().check(constraints_to_constraintset(cons))
|
||||
|
||||
|
||||
def get_new_constrs_for_queue(oldcons, newcons):
|
||||
ret = []
|
||||
|
||||
@@ -225,14 +242,19 @@ def get_new_constrs_for_queue(oldcons, newcons):
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def inp2ints(inp):
|
||||
a, b, c = struct.unpack('<iii', inp)
|
||||
return f'a={a} b={b} c={c}'
|
||||
a, b, c = struct.unpack("<iii", inp)
|
||||
return f"a={a} b={b} c={c}"
|
||||
|
||||
|
||||
def ints2inp(*ints):
|
||||
return struct.pack('<'+'i'*len(ints), *ints)
|
||||
return struct.pack("<" + "i" * len(ints), *ints)
|
||||
|
||||
|
||||
traces = set()
|
||||
|
||||
|
||||
def concrete_input_to_constraints(ci, prev=None):
|
||||
global traces
|
||||
if prev is None:
|
||||
@@ -240,11 +262,13 @@ def concrete_input_to_constraints(ci, prev=None):
|
||||
trc = concrete_run_get_trace(ci)
|
||||
|
||||
# Only heed new traces
|
||||
trace_rips = tuple(x['values']['RIP'] for x in trc if x['type'] == 'regs' and 'RIP' in x['values'])
|
||||
trace_rips = tuple(
|
||||
x["values"]["RIP"] for x in trc if x["type"] == "regs" and "RIP" in x["values"]
|
||||
)
|
||||
if trace_rips in traces:
|
||||
return [], []
|
||||
traces.add(trace_rips)
|
||||
|
||||
|
||||
log("getting constraints from symbolic run")
|
||||
cons, datas = symbolic_run_get_cons(trc)
|
||||
# hmmm: ideally, do some smart stuff so we don't have to check if the
|
||||
@@ -255,7 +279,7 @@ def concrete_input_to_constraints(ci, prev=None):
|
||||
# but maybe a dumb way where we blindly permute the constraints
|
||||
# and just check if they're sat before queueing will work
|
||||
new_constraints = get_new_constrs_for_queue(prev, cons)
|
||||
log(f'permuting constraints and adding {len(new_constraints)} constraints sets to queue')
|
||||
log(f"permuting constraints and adding {len(new_constraints)} constraints sets to queue")
|
||||
return new_constraints, datas
|
||||
|
||||
|
||||
@@ -266,7 +290,7 @@ def main():
|
||||
# todo randomly generated concrete start
|
||||
stdin = ints2inp(0, 5, 0)
|
||||
|
||||
log(f'seed input generated ({inp2ints(stdin)}), running initial concrete run.')
|
||||
log(f"seed input generated ({inp2ints(stdin)}), running initial concrete run.")
|
||||
|
||||
to_queue, datas = concrete_input_to_constraints(stdin)
|
||||
for each in to_queue:
|
||||
@@ -275,7 +299,7 @@ def main():
|
||||
# hmmm: probably issues with the datas stuff here? probably need to store
|
||||
# the datas in the queue or something. what if there was a new read(2) deep in the program, changing the datas?
|
||||
while not q.empty():
|
||||
log(f'get constraint set from queue, queue size: {q.qsize()}')
|
||||
log(f"get constraint set from queue, queue size: {q.qsize()}")
|
||||
cons = q.get()
|
||||
inp = input_from_cons(cons, datas)
|
||||
to_queue, new_datas = concrete_input_to_constraints(inp, cons)
|
||||
@@ -285,7 +309,8 @@ def main():
|
||||
for each in to_queue:
|
||||
q.put(each)
|
||||
|
||||
log(f'paths found: {len(traces)}')
|
||||
log(f"paths found: {len(traces)}")
|
||||
|
||||
if __name__=='__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -3,27 +3,27 @@
|
||||
import sys
|
||||
from manticore.native import Manticore
|
||||
|
||||
'''
|
||||
"""
|
||||
Count the number of emulated instructions.
|
||||
|
||||
This example uses the context property of the Manticore object to store data
|
||||
that's updated by the hook function. Manticore.context is needed to properly
|
||||
share data when running with multiple worker processes.
|
||||
'''
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
sys.stderr.write(f"Usage: {sys.argv[0]} [binary]\n")
|
||||
sys.exit(2)
|
||||
|
||||
m = Manticore(sys.argv[1])
|
||||
with m.locked_context() as context:
|
||||
context['count'] = 0
|
||||
context["count"] = 0
|
||||
|
||||
@m.hook(None)
|
||||
def explore(state):
|
||||
with m.locked_context() as context:
|
||||
context['count'] += 1
|
||||
context["count"] += 1
|
||||
|
||||
m.run()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import sys
|
||||
from manticore import issymbolic
|
||||
from manticore.native import Manticore
|
||||
|
||||
'''
|
||||
"""
|
||||
Replaces a variable that controls program flow with a tainted symbolic value. This
|
||||
in turn explores all possible states under that variable's influence, and reports the
|
||||
specific cmp/test instructions can be influenced by tainted data.
|
||||
@@ -29,24 +29,24 @@ Usage:
|
||||
400ab0: cmp eax, 0x44
|
||||
400b6a: cmp eax, 0xf0000
|
||||
Analysis finished. See ./mcore_cz3Jzp for results.
|
||||
'''
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 3:
|
||||
sys.stderr.write(f"Usage: {sys.argv[0]} [binary] [address]\n")
|
||||
sys.exit(2)
|
||||
|
||||
# Passing a parameter to state_explore binary disables reading the value
|
||||
# from STDIN, and relies on us adding it manually
|
||||
m = Manticore(sys.argv[1], ['anything'])
|
||||
m = Manticore(sys.argv[1], ["anything"])
|
||||
|
||||
# Uncomment to see debug output
|
||||
#m.verbosity = 2
|
||||
# m.verbosity = 2
|
||||
|
||||
# Set to the address of the instruction before the first conditional.
|
||||
introduce_at = int(sys.argv[2], 0)
|
||||
|
||||
taint_id = 'taint_A'
|
||||
taint_id = "taint_A"
|
||||
|
||||
@m.hook(introduce_at)
|
||||
def introduce_sym(state):
|
||||
@@ -61,7 +61,7 @@ if __name__ == '__main__':
|
||||
print(f"introducing symbolic value to {state.cpu.RBP-0xc:x}")
|
||||
|
||||
val = state.new_symbolic_value(32, taint=(taint_id,))
|
||||
state.cpu.write_int(state.cpu.RBP - 0xc, val, 32)
|
||||
state.cpu.write_int(state.cpu.RBP - 0xC, val, 32)
|
||||
|
||||
def has_tainted_operands(operands, taint_id):
|
||||
# type: (list[manticore.core.cpu.abstractcpu.Operand], object) -> bool
|
||||
@@ -72,16 +72,17 @@ if __name__ == '__main__':
|
||||
return False
|
||||
|
||||
every_instruction = None
|
||||
|
||||
@m.hook(every_instruction)
|
||||
def check_taint(state):
|
||||
insn = state.cpu.instruction # type: capstone.CsInsn
|
||||
if insn is None:
|
||||
return
|
||||
if insn.mnemonic in ('cmp', 'test'):
|
||||
if insn.mnemonic in ("cmp", "test"):
|
||||
if has_tainted_operands(insn.operands, taint_id):
|
||||
print(f'{insn.address:x}: {insn.mnemonic} {insn.op_str}')
|
||||
print(f"{insn.address:x}: {insn.mnemonic} {insn.op_str}")
|
||||
|
||||
print('Tainted Control Flow:')
|
||||
print("Tainted Control Flow:")
|
||||
m.run()
|
||||
|
||||
print(f'Analysis finished. See {m.workspace} for results.')
|
||||
print(f"Analysis finished. See {m.workspace} for results.")
|
||||
|
||||
@@ -4,26 +4,26 @@ import sys
|
||||
|
||||
from manticore.native import Manticore
|
||||
|
||||
'''
|
||||
"""
|
||||
Solves modified version of baby-re, compiled for arm.
|
||||
'''
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
path = sys.argv[1]
|
||||
m = Manticore(path)
|
||||
|
||||
@m.hook(0x109f0)
|
||||
@m.hook(0x109F0)
|
||||
def myhook(state):
|
||||
flag = ''
|
||||
flag = ""
|
||||
cpu = state.cpu
|
||||
arraytop = cpu.R11
|
||||
base = arraytop - 0x18
|
||||
for i in range(4):
|
||||
symbolic_input = cpu.read_int(base + i*4)
|
||||
symbolic_input = cpu.read_int(base + i * 4)
|
||||
# TODO apis to constrain input to ascii
|
||||
concrete_input = state.solve_one(symbolic_input)
|
||||
flag += chr(concrete_input & 0xff)
|
||||
print('flag is:', flag)
|
||||
flag += chr(concrete_input & 0xFF)
|
||||
print("flag is:", flag)
|
||||
m.terminate()
|
||||
|
||||
m.run()
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
import sys
|
||||
from manticore.native import Manticore
|
||||
|
||||
'''
|
||||
"""
|
||||
Demonstrates the ability to set a basic hook on a specific program counter and
|
||||
the ability to read from memory.
|
||||
'''
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
path = sys.argv[1]
|
||||
pc = int(sys.argv[2], 0)
|
||||
|
||||
@@ -26,4 +26,3 @@ if __name__ == '__main__':
|
||||
print(f"Instruction bytes: {instruction:08x}")
|
||||
|
||||
m.run()
|
||||
|
||||
|
||||
@@ -8,9 +8,8 @@ from manticore.native import Manticore
|
||||
# and producing basic information about the paths explored
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
path = sys.argv[1]
|
||||
# Create a new Manticore object
|
||||
m = Manticore(path)
|
||||
m.run()
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import sys
|
||||
from manticore.native import Manticore
|
||||
|
||||
'''
|
||||
"""
|
||||
Demonstrates the ability to guide Manticore's state exploration. In this case,
|
||||
abandoning a state we're no longer interested in.
|
||||
|
||||
@@ -13,9 +13,9 @@ Usage:
|
||||
$ ADDRESS=0x$(objdump -S state_explore | grep -A 1 'value == 0x41' | tail -n 1 | sed 's|^\s*||g' | cut -f1 -d:)
|
||||
$ python ./state_control.py state_explore $ADDRESS
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 3:
|
||||
sys.stderr.write(f"Usage: {sys.argv[0]} [binary] [address]\n")
|
||||
sys.exit(2)
|
||||
@@ -23,7 +23,7 @@ if __name__ == '__main__':
|
||||
m = Manticore(sys.argv[1])
|
||||
|
||||
# Uncomment to see debug output
|
||||
#m.verbosity = 2
|
||||
# m.verbosity = 2
|
||||
|
||||
# Set to the address of the conditional at state_explore.c:38, which will be
|
||||
# abandoned. If line 36 of this script is commented out, Manticore will
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3, 6):
|
||||
print('Manticore requires Python 3.6 or higher.')
|
||||
print("Manticore requires Python 3.6 or higher.")
|
||||
sys.exit(-1)
|
||||
|
||||
from .utils import config, log
|
||||
from .utils.log import set_verbosity
|
||||
from .utils.helpers import issymbolic, istainted
|
||||
from .ethereum.manticore import ManticoreEVM
|
||||
|
||||
__all__ = [issymbolic.__name__, istainted.__name__, ManticoreEVM.__name__, set_verbosity.__name__]
|
||||
|
||||
+154
-90
@@ -11,14 +11,13 @@ from .core.manticore import ManticoreBase, set_verbosity
|
||||
from .ethereum.cli import ethereum_main
|
||||
from .utils import config, log, install_helper
|
||||
|
||||
consts = config.get_group('main')
|
||||
consts.add('recursionlimit', default=10000,
|
||||
description="Value to set for Python recursion limit")
|
||||
consts = config.get_group("main")
|
||||
consts.add("recursionlimit", default=10000, description="Value to set for Python recursion limit")
|
||||
|
||||
|
||||
# XXX(yan): This would normally be __name__, but then logger output will be pre-
|
||||
# pended by 'm.__main__: ', which is not very pleasing. hard-coding to 'main'
|
||||
logger = logging.getLogger('manticore.main')
|
||||
logger = logging.getLogger("manticore.main")
|
||||
|
||||
if install_helper.has_native:
|
||||
from manticore.native.cli import native_main
|
||||
@@ -37,7 +36,7 @@ def main():
|
||||
|
||||
set_verbosity(args.v)
|
||||
|
||||
if args.argv[0].endswith('.sol'):
|
||||
if args.argv[0].endswith(".sol"):
|
||||
ethereum_main(args, logger)
|
||||
else:
|
||||
install_helper.ensure_native_deps()
|
||||
@@ -51,104 +50,168 @@ def parse_arguments():
|
||||
raise argparse.ArgumentTypeError("Argument must be positive")
|
||||
return ivalue
|
||||
|
||||
parser = argparse.ArgumentParser(description='Symbolic execution tool', prog='manticore',
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument('--context', type=str, default=None,
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument('--coverage', type=str, default='visited.txt',
|
||||
help='Where to write the coverage data')
|
||||
parser.add_argument('--names', type=str, default=None,
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument('--no-colors', action='store_true',
|
||||
help='Disable ANSI color escape sequences in output')
|
||||
parser.add_argument('--offset', type=int, default=16,
|
||||
help=argparse.SUPPRESS)
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Symbolic execution tool",
|
||||
prog="manticore",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
parser.add_argument("--context", type=str, default=None, help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
"--coverage", type=str, default="visited.txt", help="Where to write the coverage data"
|
||||
)
|
||||
parser.add_argument("--names", type=str, default=None, help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
"--no-colors", action="store_true", help="Disable ANSI color escape sequences in output"
|
||||
)
|
||||
parser.add_argument("--offset", type=int, default=16, help=argparse.SUPPRESS)
|
||||
# FIXME (theo) Add some documentation on the different search policy options
|
||||
parser.add_argument('--policy', type=str, default='random',
|
||||
help=("Search policy. random|adhoc|uncovered|dicount"
|
||||
"|icount|syscount|depth. (use + (max) or - (min)"
|
||||
" to specify order. e.g. +random)"))
|
||||
parser.add_argument('argv', type=str, nargs='*', default=[],
|
||||
help="Path to program, and arguments ('+' in arguments indicates symbolic byte).")
|
||||
parser.add_argument('-v', action='count', default=1,
|
||||
help='Specify verbosity level from -v to -vvvv')
|
||||
parser.add_argument('--workspace', type=str, default=None,
|
||||
help=("A folder name for temporaries and results."
|
||||
"(default mcore_?????)"))
|
||||
parser.add_argument(
|
||||
"--policy",
|
||||
type=str,
|
||||
default="random",
|
||||
help=(
|
||||
"Search policy. random|adhoc|uncovered|dicount"
|
||||
"|icount|syscount|depth. (use + (max) or - (min)"
|
||||
" to specify order. e.g. +random)"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"argv",
|
||||
type=str,
|
||||
nargs="*",
|
||||
default=[],
|
||||
help="Path to program, and arguments ('+' in arguments indicates symbolic byte).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v", action="count", default=1, help="Specify verbosity level from -v to -vvvv"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--workspace",
|
||||
type=str,
|
||||
default=None,
|
||||
help=("A folder name for temporaries and results." "(default mcore_?????)"),
|
||||
)
|
||||
|
||||
current_version = pkg_resources.get_distribution("manticore").version
|
||||
parser.add_argument('--version', action='version', version=f'Manticore {current_version}',
|
||||
help='Show program version information')
|
||||
parser.add_argument('--config', type=str,
|
||||
help='Manticore config file (.yml) to use. (default config file pattern is: ./[.]m[anti]core.yml)')
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version=f"Manticore {current_version}",
|
||||
help="Show program version information",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--config",
|
||||
type=str,
|
||||
help="Manticore config file (.yml) to use. (default config file pattern is: ./[.]m[anti]core.yml)",
|
||||
)
|
||||
|
||||
bin_flags = parser.add_argument_group('Binary flags')
|
||||
bin_flags.add_argument('--entrysymbol', type=str, default=None,
|
||||
help='Symbol as entry point')
|
||||
bin_flags.add_argument('--assertions', type=str, default=None,
|
||||
help=argparse.SUPPRESS)
|
||||
bin_flags.add_argument('--buffer', type=str,
|
||||
help=argparse.SUPPRESS)
|
||||
bin_flags.add_argument('--data', type=str, default='',
|
||||
help='Initial concrete concrete_data for the input symbolic buffer')
|
||||
bin_flags.add_argument('--file', type=str, default=[], action='append', dest='files',
|
||||
help='Specify symbolic input file, \'+\' marks symbolic bytes')
|
||||
bin_flags.add_argument('--env', type=str, nargs=1, default=[], action='append',
|
||||
help='Add an environment variable. Use "+" for symbolic bytes. (VARNAME=++++)')
|
||||
bin_flags.add_argument('--pure-symbolic', action='store_true',
|
||||
help='Treat all writable memory as symbolic')
|
||||
bin_flags = parser.add_argument_group("Binary flags")
|
||||
bin_flags.add_argument("--entrysymbol", type=str, default=None, help="Symbol as entry point")
|
||||
bin_flags.add_argument("--assertions", type=str, default=None, help=argparse.SUPPRESS)
|
||||
bin_flags.add_argument("--buffer", type=str, help=argparse.SUPPRESS)
|
||||
bin_flags.add_argument(
|
||||
"--data",
|
||||
type=str,
|
||||
default="",
|
||||
help="Initial concrete concrete_data for the input symbolic buffer",
|
||||
)
|
||||
bin_flags.add_argument(
|
||||
"--file",
|
||||
type=str,
|
||||
default=[],
|
||||
action="append",
|
||||
dest="files",
|
||||
help="Specify symbolic input file, '+' marks symbolic bytes",
|
||||
)
|
||||
bin_flags.add_argument(
|
||||
"--env",
|
||||
type=str,
|
||||
nargs=1,
|
||||
default=[],
|
||||
action="append",
|
||||
help='Add an environment variable. Use "+" for symbolic bytes. (VARNAME=++++)',
|
||||
)
|
||||
bin_flags.add_argument(
|
||||
"--pure-symbolic", action="store_true", help="Treat all writable memory as symbolic"
|
||||
)
|
||||
|
||||
eth_flags = parser.add_argument_group('Ethereum flags')
|
||||
eth_flags.add_argument('--verbose-trace', action='store_true',
|
||||
help='Dump an extra verbose trace for each state')
|
||||
eth_flags.add_argument('--txlimit', type=positive,
|
||||
help='Maximum number of symbolic transactions to run (positive integer)')
|
||||
eth_flags = parser.add_argument_group("Ethereum flags")
|
||||
eth_flags.add_argument(
|
||||
"--verbose-trace", action="store_true", help="Dump an extra verbose trace for each state"
|
||||
)
|
||||
eth_flags.add_argument(
|
||||
"--txlimit",
|
||||
type=positive,
|
||||
help="Maximum number of symbolic transactions to run (positive integer)",
|
||||
)
|
||||
|
||||
eth_flags.add_argument('--txnocoverage', action='store_true',
|
||||
help='Do not use coverage as stopping criteria')
|
||||
eth_flags.add_argument(
|
||||
"--txnocoverage", action="store_true", help="Do not use coverage as stopping criteria"
|
||||
)
|
||||
|
||||
eth_flags.add_argument('--txnoether', action='store_true',
|
||||
help='Do not attempt to send ether to contract')
|
||||
eth_flags.add_argument(
|
||||
"--txnoether", action="store_true", help="Do not attempt to send ether to contract"
|
||||
)
|
||||
|
||||
eth_flags.add_argument('--txaccount', type=str, default="attacker",
|
||||
help='Account used as caller in the symbolic transactions, either "attacker" or '
|
||||
'"owner" or "combo1" (uses both)')
|
||||
eth_flags.add_argument(
|
||||
"--txaccount",
|
||||
type=str,
|
||||
default="attacker",
|
||||
help='Account used as caller in the symbolic transactions, either "attacker" or '
|
||||
'"owner" or "combo1" (uses both)',
|
||||
)
|
||||
|
||||
eth_flags.add_argument('--txpreconstrain', action='store_true',
|
||||
help='Constrain human transactions to avoid exceptions in the contract function dispatcher')
|
||||
eth_flags.add_argument(
|
||||
"--txpreconstrain",
|
||||
action="store_true",
|
||||
help="Constrain human transactions to avoid exceptions in the contract function dispatcher",
|
||||
)
|
||||
|
||||
eth_flags.add_argument('--contract', type=str,
|
||||
help='Contract name to analyze in case of multiple contracts')
|
||||
eth_flags.add_argument(
|
||||
"--contract", type=str, help="Contract name to analyze in case of multiple contracts"
|
||||
)
|
||||
|
||||
eth_detectors = parser.add_argument_group('Ethereum detectors')
|
||||
eth_detectors = parser.add_argument_group("Ethereum detectors")
|
||||
|
||||
eth_detectors.add_argument('--list-detectors',
|
||||
help='List available detectors',
|
||||
action=ListEthereumDetectors,
|
||||
nargs=0,
|
||||
default=False)
|
||||
eth_detectors.add_argument(
|
||||
"--list-detectors",
|
||||
help="List available detectors",
|
||||
action=ListEthereumDetectors,
|
||||
nargs=0,
|
||||
default=False,
|
||||
)
|
||||
|
||||
eth_detectors.add_argument('--exclude',
|
||||
help='Comma-separated list of detectors that should be excluded',
|
||||
action='store',
|
||||
dest='detectors_to_exclude',
|
||||
default='')
|
||||
eth_detectors.add_argument(
|
||||
"--exclude",
|
||||
help="Comma-separated list of detectors that should be excluded",
|
||||
action="store",
|
||||
dest="detectors_to_exclude",
|
||||
default="",
|
||||
)
|
||||
|
||||
eth_detectors.add_argument('--exclude-all',
|
||||
help='Excludes all detectors',
|
||||
action='store_true',
|
||||
default=False)
|
||||
eth_detectors.add_argument(
|
||||
"--exclude-all", help="Excludes all detectors", action="store_true", default=False
|
||||
)
|
||||
|
||||
eth_flags.add_argument('--avoid-constant', action='store_true',
|
||||
help='Avoid exploring constant functions for human transactions')
|
||||
eth_flags.add_argument(
|
||||
"--avoid-constant",
|
||||
action="store_true",
|
||||
help="Avoid exploring constant functions for human transactions",
|
||||
)
|
||||
|
||||
eth_flags.add_argument('--limit-loops', action='store_true',
|
||||
help='Avoid exploring constant functions for human transactions')
|
||||
eth_flags.add_argument(
|
||||
"--limit-loops",
|
||||
action="store_true",
|
||||
help="Avoid exploring constant functions for human transactions",
|
||||
)
|
||||
|
||||
eth_flags.add_argument('--no-testcases', action='store_true',
|
||||
help='Do not generate testcases for discovered states when analysis finishes')
|
||||
eth_flags.add_argument(
|
||||
"--no-testcases",
|
||||
action="store_true",
|
||||
help="Do not generate testcases for discovered states when analysis finishes",
|
||||
)
|
||||
|
||||
config_flags = parser.add_argument_group('Constants')
|
||||
config_flags = parser.add_argument_group("Constants")
|
||||
config.add_config_vars_to_argparse(config_flags)
|
||||
|
||||
parsed = parser.parse_args(sys.argv[1:])
|
||||
@@ -158,10 +221,10 @@ def parse_arguments():
|
||||
print(parser.format_usage() + "error: the following arguments are required: argv")
|
||||
sys.exit(1)
|
||||
|
||||
if parsed.policy.startswith('min'):
|
||||
parsed.policy = '-' + parsed.policy[3:]
|
||||
elif parsed.policy.startswith('max'):
|
||||
parsed.policy = '+' + parsed.policy[3:]
|
||||
if parsed.policy.startswith("min"):
|
||||
parsed.policy = "-" + parsed.policy[3:]
|
||||
elif parsed.policy.startswith("max"):
|
||||
parsed.policy = "+" + parsed.policy[3:]
|
||||
|
||||
return parsed
|
||||
|
||||
@@ -170,9 +233,10 @@ class ListEthereumDetectors(argparse.Action):
|
||||
def __call__(self, parser, *args, **kwargs):
|
||||
from .ethereum.cli import get_detectors_classes
|
||||
from .utils.command_line import output_detectors
|
||||
|
||||
output_detectors(get_detectors_classes())
|
||||
parser.exit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -18,7 +18,8 @@ and common API. interpreters? linkers? linked DLLs?
|
||||
from .binary import Binary, CGCElf, Elf # noqa
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
print(list(Binary(sys.argv[1]).threads()))
|
||||
print(list(Binary(sys.argv[1]).maps()))
|
||||
|
||||
+38
-28
@@ -8,7 +8,7 @@ class Binary:
|
||||
|
||||
def __new__(cls, path):
|
||||
if cls is Binary:
|
||||
with open(path, 'rb') as f:
|
||||
with open(path, "rb") as f:
|
||||
cl = cls.magics[f.read(4)]
|
||||
return cl(path)
|
||||
else:
|
||||
@@ -16,7 +16,7 @@ class Binary:
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
with open(path, 'rb') as f:
|
||||
with open(path, "rb") as f:
|
||||
self.magic = Binary.magics[f.read(4)]
|
||||
|
||||
def arch(self):
|
||||
@@ -30,13 +30,12 @@ class Binary:
|
||||
|
||||
|
||||
class CGCElf(Binary):
|
||||
|
||||
@staticmethod
|
||||
def _cgc2elf(filename):
|
||||
# hack begin so we can use upstream Elftool
|
||||
with open(filename, 'rb') as fd:
|
||||
with open(filename, "rb") as fd:
|
||||
stream = io.BytesIO(fd.read())
|
||||
stream.write(b'\x7fELF')
|
||||
stream.write(b"\x7fELF")
|
||||
stream.name = fd.name
|
||||
return stream
|
||||
|
||||
@@ -44,78 +43,89 @@ class CGCElf(Binary):
|
||||
super().__init__(filename)
|
||||
stream = self._cgc2elf(filename)
|
||||
self.elf = ELFFile(stream)
|
||||
self.arch = {'x86': 'i386', 'x64': 'amd64'}[self.elf.get_machine_arch()]
|
||||
self.arch = {"x86": "i386", "x64": "amd64"}[self.elf.get_machine_arch()]
|
||||
|
||||
assert 'i386' == self.arch
|
||||
assert self.elf.header.e_type in ['ET_EXEC']
|
||||
assert "i386" == self.arch
|
||||
assert self.elf.header.e_type in ["ET_EXEC"]
|
||||
|
||||
def maps(self):
|
||||
for elf_segment in self.elf.iter_segments():
|
||||
if elf_segment.header.p_type not in ['PT_LOAD', 'PT_NULL', 'PT_PHDR', 'PT_CGCPOV2']:
|
||||
if elf_segment.header.p_type not in ["PT_LOAD", "PT_NULL", "PT_PHDR", "PT_CGCPOV2"]:
|
||||
raise Exception("Not Supported Section")
|
||||
|
||||
if elf_segment.header.p_type != 'PT_LOAD' or elf_segment.header.p_memsz == 0:
|
||||
if elf_segment.header.p_type != "PT_LOAD" or elf_segment.header.p_memsz == 0:
|
||||
continue
|
||||
|
||||
flags = elf_segment.header.p_flags
|
||||
# PF_X 0x1 Execute - PF_W 0x2 Write - PF_R 0x4 Read
|
||||
perms = [' ', ' x', ' w ', ' wx', 'r ', 'r x', 'rw ', 'rwx'][flags & 7]
|
||||
if 'r' not in perms:
|
||||
perms = [" ", " x", " w ", " wx", "r ", "r x", "rw ", "rwx"][flags & 7]
|
||||
if "r" not in perms:
|
||||
raise Exception("Not readable map from cgc elf not supported")
|
||||
|
||||
# CGCMAP--
|
||||
assert elf_segment.header.p_filesz != 0 or elf_segment.header.p_memsz != 0
|
||||
yield ((elf_segment.header.p_vaddr,
|
||||
yield (
|
||||
(
|
||||
elf_segment.header.p_vaddr,
|
||||
elf_segment.header.p_memsz,
|
||||
perms,
|
||||
elf_segment.stream.name, elf_segment.header.p_offset, elf_segment.header.p_filesz))
|
||||
elf_segment.stream.name,
|
||||
elf_segment.header.p_offset,
|
||||
elf_segment.header.p_filesz,
|
||||
)
|
||||
)
|
||||
|
||||
def threads(self):
|
||||
yield (('Running', {'EIP': self.elf.header.e_entry}))
|
||||
yield (("Running", {"EIP": self.elf.header.e_entry}))
|
||||
|
||||
|
||||
class Elf(Binary):
|
||||
def __init__(self, filename):
|
||||
super().__init__(filename)
|
||||
self.elf = ELFFile(open(filename, 'rb'))
|
||||
self.arch = {'x86': 'i386', 'x64': 'amd64'}[self.elf.get_machine_arch()]
|
||||
assert self.elf.header.e_type in ['ET_DYN', 'ET_EXEC', 'ET_CORE']
|
||||
self.elf = ELFFile(open(filename, "rb"))
|
||||
self.arch = {"x86": "i386", "x64": "amd64"}[self.elf.get_machine_arch()]
|
||||
assert self.elf.header.e_type in ["ET_DYN", "ET_EXEC", "ET_CORE"]
|
||||
|
||||
# Get interpreter elf
|
||||
self.interpreter = None
|
||||
for elf_segment in self.elf.iter_segments():
|
||||
if elf_segment.header.p_type != 'PT_INTERP':
|
||||
if elf_segment.header.p_type != "PT_INTERP":
|
||||
continue
|
||||
self.interpreter = Elf(elf_segment.data()[:-1])
|
||||
break
|
||||
if self.interpreter is not None:
|
||||
assert self.interpreter.arch == self.arch
|
||||
assert self.interpreter.elf.header.e_type in ['ET_DYN', 'ET_EXEC']
|
||||
assert self.interpreter.elf.header.e_type in ["ET_DYN", "ET_EXEC"]
|
||||
|
||||
def maps(self):
|
||||
for elf_segment in self.elf.iter_segments():
|
||||
if elf_segment.header.p_type != 'PT_LOAD' or elf_segment.header.p_memsz == 0:
|
||||
if elf_segment.header.p_type != "PT_LOAD" or elf_segment.header.p_memsz == 0:
|
||||
continue
|
||||
|
||||
flags = elf_segment.header.p_flags
|
||||
# PF_X 0x1 Execute - PF_W 0x2 Write - PF_R 0x4 Read
|
||||
perms = [' ', ' x', ' w ', ' wx', 'r ', 'r x', 'rw ', 'rwx'][flags & 7]
|
||||
if 'r' not in perms:
|
||||
perms = [" ", " x", " w ", " wx", "r ", "r x", "rw ", "rwx"][flags & 7]
|
||||
if "r" not in perms:
|
||||
raise Exception("Not readable map from cgc elf not supported")
|
||||
|
||||
# CGCMAP--
|
||||
assert elf_segment.header.p_filesz != 0 or elf_segment.header.p_memsz != 0
|
||||
yield ((elf_segment.header.p_vaddr,
|
||||
yield (
|
||||
(
|
||||
elf_segment.header.p_vaddr,
|
||||
elf_segment.header.p_memsz,
|
||||
perms,
|
||||
elf_segment.stream.name, elf_segment.header.p_offset, elf_segment.header.p_filesz))
|
||||
elf_segment.stream.name,
|
||||
elf_segment.header.p_offset,
|
||||
elf_segment.header.p_filesz,
|
||||
)
|
||||
)
|
||||
|
||||
def getInterpreter(self):
|
||||
return self.interpreter
|
||||
|
||||
def threads(self):
|
||||
yield (('Running', {'EIP': self.elf.header.e_entry}))
|
||||
yield (("Running", {"EIP": self.elf.header.e_entry}))
|
||||
|
||||
|
||||
Binary.magics = {b'\x7fCGC': CGCElf,
|
||||
b'\x7fELF': Elf}
|
||||
Binary.magics = {b"\x7fCGC": CGCElf, b"\x7fELF": Elf}
|
||||
|
||||
+109
-62
@@ -31,9 +31,10 @@ from enum import Enum
|
||||
|
||||
class MProcessingType(Enum):
|
||||
"""Used as configuration constant for choosing multiprocessing flavor"""
|
||||
multiprocessing = 'multiprocessing'
|
||||
single = 'single'
|
||||
threading = 'threading'
|
||||
|
||||
multiprocessing = "multiprocessing"
|
||||
single = "single"
|
||||
threading = "threading"
|
||||
|
||||
def title(self):
|
||||
return self._name_.title()
|
||||
@@ -43,27 +44,34 @@ class MProcessingType(Enum):
|
||||
return cls.__members__[name]
|
||||
|
||||
def to_class(self):
|
||||
return globals()[f'Manticore{self.title()}']
|
||||
return globals()[f"Manticore{self.title()}"]
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
consts = config.get_group('core')
|
||||
consts.add('timeout', default=0, description='Timeout, in seconds, for Manticore invocation')
|
||||
consts.add('cluster', default=False, description='If True enables to run workers over the network UNIMPLEMENTED')
|
||||
consts.add('procs', default=10, description='Number of parallel processes to spawn')
|
||||
consts.add('mprocessing', default=MProcessingType.multiprocessing, description='single: No multiprocessing at all. Single process.\n threading: use threads\m multiprocessing: use forked processes')
|
||||
consts = config.get_group("core")
|
||||
consts.add("timeout", default=0, description="Timeout, in seconds, for Manticore invocation")
|
||||
consts.add(
|
||||
"cluster",
|
||||
default=False,
|
||||
description="If True enables to run workers over the network UNIMPLEMENTED",
|
||||
)
|
||||
consts.add("procs", default=10, description="Number of parallel processes to spawn")
|
||||
consts.add(
|
||||
"mprocessing",
|
||||
default=MProcessingType.multiprocessing,
|
||||
description="single: No multiprocessing at all. Single process.\n threading: use threads\m multiprocessing: use forked processes",
|
||||
)
|
||||
|
||||
|
||||
class ManticoreBase(Eventful):
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls in (ManticoreBase, ManticoreSingle, ManticoreThreading, ManticoreMultiprocessing):
|
||||
raise Exception("Should not instantiate this")
|
||||
|
||||
cl = consts.mprocessing.to_class()
|
||||
if cl not in cls.__bases__:
|
||||
#change ManticoreBase for the more specific class
|
||||
# change ManticoreBase for the more specific class
|
||||
idx = cls.__bases__.index(ManticoreBase)
|
||||
bases = list(cls.__bases__)
|
||||
bases[idx] = cl
|
||||
@@ -74,6 +82,7 @@ class ManticoreBase(Eventful):
|
||||
# Decorators added first for convenience.
|
||||
def sync(func):
|
||||
"""Synchronization decorator"""
|
||||
|
||||
@functools.wraps(func)
|
||||
def newFunction(self, *args, **kw):
|
||||
with self._lock:
|
||||
@@ -85,6 +94,7 @@ class ManticoreBase(Eventful):
|
||||
"""Allows the decorated method to run only when manticore is actively
|
||||
exploring states
|
||||
"""
|
||||
|
||||
@functools.wraps(func)
|
||||
def newFunction(self, *args, **kw):
|
||||
if not self.is_running():
|
||||
@@ -106,10 +116,19 @@ class ManticoreBase(Eventful):
|
||||
|
||||
return newFunction
|
||||
|
||||
_published_events = {'run', 'start_worker', 'terminate_worker', 'enqueue_state', 'fork_state', 'load_state',
|
||||
'terminate_state', 'kill_state', 'execute_instruction'}
|
||||
_published_events = {
|
||||
"run",
|
||||
"start_worker",
|
||||
"terminate_worker",
|
||||
"enqueue_state",
|
||||
"fork_state",
|
||||
"load_state",
|
||||
"terminate_state",
|
||||
"kill_state",
|
||||
"execute_instruction",
|
||||
}
|
||||
|
||||
def __init__(self, initial_state, workspace_url=None, policy='random', **kwargs):
|
||||
def __init__(self, initial_state, workspace_url=None, policy="random", **kwargs):
|
||||
"""
|
||||
:param initial_state: State to start from.
|
||||
|
||||
@@ -220,8 +239,21 @@ class ManticoreBase(Eventful):
|
||||
"""
|
||||
super().__init__()
|
||||
|
||||
if any(not hasattr(self, x) for x in ('_worker_type', '_lock', '_running', '_killed', '_ready_states', '_terminated_states', '_killed_states', '_busy_states', '_shared_context')):
|
||||
raise Exception('Need to instantiate one of: ManticoreNative, ManticoreThreads..')
|
||||
if any(
|
||||
not hasattr(self, x)
|
||||
for x in (
|
||||
"_worker_type",
|
||||
"_lock",
|
||||
"_running",
|
||||
"_killed",
|
||||
"_ready_states",
|
||||
"_terminated_states",
|
||||
"_killed_states",
|
||||
"_busy_states",
|
||||
"_shared_context",
|
||||
)
|
||||
):
|
||||
raise Exception("Need to instantiate one of: ManticoreNative, ManticoreThreads..")
|
||||
|
||||
# The workspace and the output
|
||||
# Manticore will use the workspace to save and share temporary states.
|
||||
@@ -229,15 +261,15 @@ class ManticoreBase(Eventful):
|
||||
# By default the output folder and the workspace folder are the same.
|
||||
# Check type, default to fs:
|
||||
if isinstance(workspace_url, str):
|
||||
if ':' not in workspace_url:
|
||||
workspace_url = f'fs:{workspace_url}'
|
||||
if ":" not in workspace_url:
|
||||
workspace_url = f"fs:{workspace_url}"
|
||||
else:
|
||||
if workspace_url is not None:
|
||||
raise TypeError(f'Invalid workspace type: {type(workspace_url).__name__}')
|
||||
raise TypeError(f"Invalid workspace type: {type(workspace_url).__name__}")
|
||||
self._workspace = Workspace(workspace_url)
|
||||
#reuse the same workspace if not specified
|
||||
# reuse the same workspace if not specified
|
||||
if workspace_url is None:
|
||||
workspace_url = f'fs:{self._workspace.uri}'
|
||||
workspace_url = f"fs:{self._workspace.uri}"
|
||||
self._output = ManticoreOutput(workspace_url)
|
||||
|
||||
# The set of registered plugins
|
||||
@@ -249,7 +281,7 @@ class ManticoreBase(Eventful):
|
||||
|
||||
# Set initial root state
|
||||
if not isinstance(initial_state, StateBase):
|
||||
raise TypeError(f'Invalid initial_state type: {type(initial_state).__name__}')
|
||||
raise TypeError(f"Invalid initial_state type: {type(initial_state).__name__}")
|
||||
self._put_state(initial_state)
|
||||
|
||||
# Workers will use manticore __dict__ So lets spawn them last
|
||||
@@ -259,7 +291,7 @@ class ManticoreBase(Eventful):
|
||||
def __str__(self):
|
||||
return f"<{str(type(self))[8:-2]}| Alive States: {self.count_ready_states()}; Running States: {self.count_busy_states()} Terminated States: {self.count_terminated_states()} Killed States: {self.count_killed_states()} Started: {self._running.value} Killed: {self._killed.value}>"
|
||||
|
||||
def _fork(self, state, expression, policy='ALL', setstate=None):
|
||||
def _fork(self, state, expression, policy="ALL", setstate=None):
|
||||
"""
|
||||
Fork state on expression concretizations.
|
||||
Using policy build a list of solutions for expression.
|
||||
@@ -284,6 +316,7 @@ class ManticoreBase(Eventful):
|
||||
assert isinstance(expression, Expression)
|
||||
|
||||
if setstate is None:
|
||||
|
||||
def setstate(x, y):
|
||||
pass
|
||||
|
||||
@@ -293,10 +326,11 @@ class ManticoreBase(Eventful):
|
||||
if not solutions:
|
||||
raise ManticoreError("Forking on unfeasible constraint set")
|
||||
|
||||
logger.debug("Forking. Policy: %s. Values: %s", policy,
|
||||
', '.join(f'0x{sol:x}' for sol in solutions))
|
||||
logger.debug(
|
||||
"Forking. Policy: %s. Values: %s", policy, ", ".join(f"0x{sol:x}" for sol in solutions)
|
||||
)
|
||||
|
||||
self._publish('will_fork_state', state, expression, solutions, policy)
|
||||
self._publish("will_fork_state", state, expression, solutions, policy)
|
||||
|
||||
# Build and enqueue a state for each solution
|
||||
children = []
|
||||
@@ -314,7 +348,7 @@ class ManticoreBase(Eventful):
|
||||
self._ready_states.append(new_state_id)
|
||||
self._lock.notify_all() # Must notify one!
|
||||
|
||||
self._publish('did_fork_state', new_state, expression, new_value, policy)
|
||||
self._publish("did_fork_state", new_state, expression, new_value, policy)
|
||||
# maintain a list of children for logging purpose
|
||||
children.append(new_state_id)
|
||||
|
||||
@@ -360,11 +394,11 @@ class ManticoreBase(Eventful):
|
||||
self.stcache = weakref.WeakValueDictionary()
|
||||
if state_id in self.stcache:
|
||||
return self.stcache[state_id]
|
||||
self._publish('will_load_state', state_id)
|
||||
self._publish("will_load_state", state_id)
|
||||
state = self._workspace.load_state(state_id, delete=False)
|
||||
state._id = state_id
|
||||
self.forward_events_from(state, True)
|
||||
self._publish('did_load_state', state, state_id)
|
||||
self._publish("did_load_state", state, state_id)
|
||||
self.stcache[state_id] = state
|
||||
return state
|
||||
|
||||
@@ -559,7 +593,9 @@ class ManticoreBase(Eventful):
|
||||
""" Only allowed at not running.
|
||||
(At running we can have states at busy)
|
||||
"""
|
||||
return tuple(self._ready_states) + tuple(self._terminated_states) + tuple(self._killed_states)
|
||||
return (
|
||||
tuple(self._ready_states) + tuple(self._terminated_states) + tuple(self._killed_states)
|
||||
)
|
||||
|
||||
@property
|
||||
@sync
|
||||
@@ -601,16 +637,16 @@ class ManticoreBase(Eventful):
|
||||
""" Terminated states count """
|
||||
return len(self._terminated_states)
|
||||
|
||||
def generate_testcase(self, state, message='test', name='test'):
|
||||
def generate_testcase(self, state, message="test", name="test"):
|
||||
testcase = self._output.testcase(prefix=name)
|
||||
with testcase.open_stream('pkl', binary=True) as statef:
|
||||
with testcase.open_stream("pkl", binary=True) as statef:
|
||||
PickleSerializer().serialize(state, statef)
|
||||
|
||||
#Let the plugins generate a state based report
|
||||
# Let the plugins generate a state based report
|
||||
for p in self.plugins:
|
||||
p.generate_testcase(state, testcase, message)
|
||||
|
||||
logger.info('Generated testcase No. %d - %s', testcase.num, message)
|
||||
logger.info("Generated testcase No. %d - %s", testcase.num, message)
|
||||
return testcase
|
||||
|
||||
@at_not_running
|
||||
@@ -618,7 +654,7 @@ class ManticoreBase(Eventful):
|
||||
# Global enumeration of valid events
|
||||
assert isinstance(plugin, Plugin)
|
||||
assert plugin not in self.plugins, "Plugin instance already registered"
|
||||
assert getattr(plugin, 'manticore', None) is None, "Plugin instance already owned"
|
||||
assert getattr(plugin, "manticore", None) is None, "Plugin instance already owned"
|
||||
|
||||
plugin.manticore = self
|
||||
self.plugins.add(plugin)
|
||||
@@ -627,35 +663,47 @@ class ManticoreBase(Eventful):
|
||||
prefix = Eventful.prefixes
|
||||
all_events = [x + y for x, y in itertools.product(prefix, events)]
|
||||
for event_name in all_events:
|
||||
callback_name = f'{event_name}_callback'
|
||||
callback_name = f"{event_name}_callback"
|
||||
callback = getattr(plugin, callback_name, None)
|
||||
if callback is not None:
|
||||
self.subscribe(event_name, callback)
|
||||
|
||||
# Safety checks
|
||||
for callback_name in dir(plugin):
|
||||
if callback_name.endswith('_callback'):
|
||||
if callback_name.endswith("_callback"):
|
||||
event_name = callback_name[:-9]
|
||||
if event_name not in all_events:
|
||||
logger.warning("There is no event named %s for callback on plugin %s", event_name,
|
||||
type(plugin).__name__)
|
||||
logger.warning(
|
||||
"There is no event named %s for callback on plugin %s",
|
||||
event_name,
|
||||
type(plugin).__name__,
|
||||
)
|
||||
|
||||
for event_name in all_events:
|
||||
for plugin_method_name in dir(plugin):
|
||||
if event_name in plugin_method_name:
|
||||
if not plugin_method_name.endswith('_callback'):
|
||||
if plugin_method_name.startswith('on_') or \
|
||||
plugin_method_name.startswith('will_') or \
|
||||
plugin_method_name.startswith('did_'):
|
||||
logger.warning("Plugin methods named '%s()' should end with '_callback' on plugin %s",
|
||||
plugin_method_name, type(plugin).__name__)
|
||||
if plugin_method_name.endswith('_callback') and \
|
||||
not plugin_method_name.startswith('on_') and \
|
||||
not plugin_method_name.startswith('will_') and \
|
||||
not plugin_method_name.startswith('did_'):
|
||||
if not plugin_method_name.endswith("_callback"):
|
||||
if (
|
||||
plugin_method_name.startswith("on_")
|
||||
or plugin_method_name.startswith("will_")
|
||||
or plugin_method_name.startswith("did_")
|
||||
):
|
||||
logger.warning(
|
||||
"Plugin methods named '%s()' should end with '_callback' on plugin %s",
|
||||
plugin_method_name,
|
||||
type(plugin).__name__,
|
||||
)
|
||||
if (
|
||||
plugin_method_name.endswith("_callback")
|
||||
and not plugin_method_name.startswith("on_")
|
||||
and not plugin_method_name.startswith("will_")
|
||||
and not plugin_method_name.startswith("did_")
|
||||
):
|
||||
logger.warning(
|
||||
"Plugin methods named '%s()' should start with 'on_', 'will_' or 'did_' on plugin %s",
|
||||
plugin_method_name, type(plugin).__name__)
|
||||
plugin_method_name,
|
||||
type(plugin).__name__,
|
||||
)
|
||||
|
||||
plugin.on_register()
|
||||
|
||||
@@ -672,6 +720,7 @@ class ManticoreBase(Eventful):
|
||||
def subscribe(self, name, callback):
|
||||
""" Register a callback to an event"""
|
||||
from types import MethodType
|
||||
|
||||
if not isinstance(callback, MethodType):
|
||||
callback = MethodType(callback, self)
|
||||
super().subscribe(name, callback)
|
||||
@@ -725,7 +774,7 @@ class ManticoreBase(Eventful):
|
||||
# if a key is provided we yield the specific value or a fresh one
|
||||
if value_type not in (list, dict):
|
||||
raise TypeError("Type must be list or dict")
|
||||
if hasattr(self, '_context_value_types'):
|
||||
if hasattr(self, "_context_value_types"):
|
||||
value_type = self._context_value_types[value_type]
|
||||
context = self._shared_context
|
||||
if key not in context:
|
||||
@@ -810,7 +859,7 @@ class ManticoreBase(Eventful):
|
||||
# User subscription to events is disabled from now on
|
||||
self.subscribe = None
|
||||
|
||||
self._publish('will_run', self.ready_states)
|
||||
self._publish("will_run", self.ready_states)
|
||||
self._running.value = True
|
||||
# start all the workers!
|
||||
for w in self._workers:
|
||||
@@ -836,7 +885,7 @@ class ManticoreBase(Eventful):
|
||||
self._killed_states.append(self._ready_states.pop())
|
||||
|
||||
self._running.value = False
|
||||
self._publish('did_run')
|
||||
self._publish("did_run")
|
||||
assert not self.is_running()
|
||||
|
||||
@sync
|
||||
@@ -871,20 +920,20 @@ class ManticoreBase(Eventful):
|
||||
############################################################################
|
||||
|
||||
def save_run_data(self):
|
||||
with self._output.save_stream('command.sh') as f:
|
||||
f.write(' '.join(map(shlex.quote, sys.argv)))
|
||||
with self._output.save_stream("command.sh") as f:
|
||||
f.write(" ".join(map(shlex.quote, sys.argv)))
|
||||
|
||||
with self._output.save_stream('manticore.yml') as f:
|
||||
with self._output.save_stream("manticore.yml") as f:
|
||||
config.save(f)
|
||||
|
||||
logger.info('Results in %s', self._output.store.uri)
|
||||
logger.info("Results in %s", self._output.store.uri)
|
||||
|
||||
|
||||
class ManticoreSingle(ManticoreBase):
|
||||
_worker_type = WorkerSingle
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
class FakeLock():
|
||||
class FakeLock:
|
||||
def _nothing(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
@@ -937,8 +986,7 @@ class ManticoreMultiprocessing(ManticoreBase):
|
||||
# This is the global manager that will handle all shared memory access
|
||||
# See. https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.SyncManager
|
||||
self._manager = SyncManager()
|
||||
self._manager.start(
|
||||
lambda: signal.signal(signal.SIGINT, signal.SIG_IGN))
|
||||
self._manager.start(lambda: signal.signal(signal.SIGINT, signal.SIG_IGN))
|
||||
# The main manticore lock. Acquire this for accessing shared objects
|
||||
# THINKME: we use the same lock to access states lists and shared contexts
|
||||
self._lock = self._manager.Condition()
|
||||
@@ -951,7 +999,6 @@ class ManticoreMultiprocessing(ManticoreBase):
|
||||
self._busy_states = self._manager.list()
|
||||
self._killed_states = self._manager.list()
|
||||
self._shared_context = self._manager.dict()
|
||||
self._context_value_types = {list: self._manager.list,
|
||||
dict: self._manager.dict}
|
||||
self._context_value_types = {list: self._manager.list, dict: self._manager.dict}
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
+126
-128
@@ -1,8 +1,8 @@
|
||||
|
||||
# Minimal INTEL assembler expression calculator
|
||||
import ply.yacc as yacc
|
||||
import copy
|
||||
from ..smtlib import Operators
|
||||
|
||||
# Lexer
|
||||
# ------------------------------------------------------------
|
||||
# calclex.py
|
||||
@@ -12,106 +12,108 @@ from ..smtlib import Operators
|
||||
# ------------------------------------------------------------
|
||||
import ply.lex as lex
|
||||
import re
|
||||
|
||||
# List of token names. This is always required
|
||||
tokens = (
|
||||
'NUMBER',
|
||||
'PLUS',
|
||||
'MINUS',
|
||||
'TIMES',
|
||||
'DIVIDE',
|
||||
'AND',
|
||||
'OR',
|
||||
'NEG',
|
||||
'LPAREN',
|
||||
'RPAREN',
|
||||
'LBRAKET',
|
||||
'RBRAKET',
|
||||
'REGISTER',
|
||||
'SEGMENT',
|
||||
'COLOM',
|
||||
'PTR',
|
||||
'TYPE',
|
||||
'RSHIFT',
|
||||
'LSHIFT',
|
||||
|
||||
'LOR',
|
||||
'LAND',
|
||||
'LNOT',
|
||||
'EQ',
|
||||
'LT',
|
||||
'LE',
|
||||
'GT',
|
||||
'GE',
|
||||
|
||||
"NUMBER",
|
||||
"PLUS",
|
||||
"MINUS",
|
||||
"TIMES",
|
||||
"DIVIDE",
|
||||
"AND",
|
||||
"OR",
|
||||
"NEG",
|
||||
"LPAREN",
|
||||
"RPAREN",
|
||||
"LBRAKET",
|
||||
"RBRAKET",
|
||||
"REGISTER",
|
||||
"SEGMENT",
|
||||
"COLOM",
|
||||
"PTR",
|
||||
"TYPE",
|
||||
"RSHIFT",
|
||||
"LSHIFT",
|
||||
"LOR",
|
||||
"LAND",
|
||||
"LNOT",
|
||||
"EQ",
|
||||
"LT",
|
||||
"LE",
|
||||
"GT",
|
||||
"GE",
|
||||
)
|
||||
|
||||
# Regular expression rules for simple tokens
|
||||
t_PLUS = r'\+'
|
||||
t_MINUS = r'-'
|
||||
t_TIMES = r'\*'
|
||||
t_DIVIDE = r'/'
|
||||
t_LPAREN = r'\('
|
||||
t_RPAREN = r'\)'
|
||||
t_LBRAKET = r'\['
|
||||
t_RBRAKET = r'\]'
|
||||
t_COLOM = r':'
|
||||
t_PLUS = r"\+"
|
||||
t_MINUS = r"-"
|
||||
t_TIMES = r"\*"
|
||||
t_DIVIDE = r"/"
|
||||
t_LPAREN = r"\("
|
||||
t_RPAREN = r"\)"
|
||||
t_LBRAKET = r"\["
|
||||
t_RBRAKET = r"\]"
|
||||
t_COLOM = r":"
|
||||
|
||||
t_AND = r'&'
|
||||
t_OR = r'\|'
|
||||
t_NEG = r'~'
|
||||
t_LSHIFT = r'<<'
|
||||
t_RSHIFT = r'>>'
|
||||
t_AND = r"&"
|
||||
t_OR = r"\|"
|
||||
t_NEG = r"~"
|
||||
t_LSHIFT = r"<<"
|
||||
t_RSHIFT = r">>"
|
||||
|
||||
t_LAND = r'&&'
|
||||
t_LOR = r'\|\|'
|
||||
t_LNOT = r'!'
|
||||
t_LAND = r"&&"
|
||||
t_LOR = r"\|\|"
|
||||
t_LNOT = r"!"
|
||||
|
||||
t_EQ = r'=='
|
||||
t_LT = r'<'
|
||||
t_LE = r'<='
|
||||
t_GT = r'>'
|
||||
t_GE = r'>='
|
||||
t_EQ = r"=="
|
||||
t_LT = r"<"
|
||||
t_LE = r"<="
|
||||
t_GT = r">"
|
||||
t_GE = r">="
|
||||
|
||||
|
||||
re_NUMBER = re.compile(r'^(0x[a-fA-F0-9]+|[a-fA-F0-9]+)$')
|
||||
re_REGISTER = re.compile(r'^(EAX|EBX|ECX|EDX|ESI|EDI|ESP|EBP|RAX|RBX|RCX|RDX|RSI|RDI|RSP|RBP|ZF|CF|SF|EFLAGS)$')
|
||||
re_SEGMENT = re.compile(r'^(SS|DS|ES|SS|CS)$')
|
||||
re_TYPE = re.compile(r'^(QWORD|DWORD|WORD|BYTE)$')
|
||||
re_PTR = re.compile(r'^PTR$')
|
||||
re_NUMBER = re.compile(r"^(0x[a-fA-F0-9]+|[a-fA-F0-9]+)$")
|
||||
re_REGISTER = re.compile(
|
||||
r"^(EAX|EBX|ECX|EDX|ESI|EDI|ESP|EBP|RAX|RBX|RCX|RDX|RSI|RDI|RSP|RBP|ZF|CF|SF|EFLAGS)$"
|
||||
)
|
||||
re_SEGMENT = re.compile(r"^(SS|DS|ES|SS|CS)$")
|
||||
re_TYPE = re.compile(r"^(QWORD|DWORD|WORD|BYTE)$")
|
||||
re_PTR = re.compile(r"^PTR$")
|
||||
|
||||
# A regular expression rule with some action code
|
||||
|
||||
|
||||
def t_TOKEN(t):
|
||||
'[a-zA-Z0-9]+'
|
||||
#print t.value,t.lexer.lexdata[t.lexer.lexpos-len(t.value):],re_TYPE.match(t.lexer.lexdata,t.lexer.lexpos-len(t.value))
|
||||
"[a-zA-Z0-9]+"
|
||||
# print t.value,t.lexer.lexdata[t.lexer.lexpos-len(t.value):],re_TYPE.match(t.lexer.lexdata,t.lexer.lexpos-len(t.value))
|
||||
if re_TYPE.match(t.value):
|
||||
t.type = 'TYPE'
|
||||
t.type = "TYPE"
|
||||
elif re_PTR.match(t.value):
|
||||
t.type = 'PTR'
|
||||
t.type = "PTR"
|
||||
elif re_NUMBER.match(t.value):
|
||||
if t.value.startswith('0x'):
|
||||
if t.value.startswith("0x"):
|
||||
t.value = t.value[2:]
|
||||
t.value = int(t.value, 16)
|
||||
t.type = 'NUMBER'
|
||||
t.type = "NUMBER"
|
||||
elif re_REGISTER.match(t.value):
|
||||
t.type = 'REGISTER'
|
||||
t.type = "REGISTER"
|
||||
elif re_SEGMENT.match(t.value):
|
||||
t.type = 'SEGMENT'
|
||||
t.type = "SEGMENT"
|
||||
else:
|
||||
raise Exception(f"Unknown:<{t.value}>")
|
||||
return t
|
||||
|
||||
|
||||
# Define a rule so we can track line numbers
|
||||
|
||||
|
||||
def t_newline(t):
|
||||
r'\n+'
|
||||
r"\n+"
|
||||
t.lexer.lineno += len(t.value)
|
||||
|
||||
|
||||
# A string containing ignored characters (spaces and tabs)
|
||||
t_ignore = ' \t'
|
||||
t_ignore = " \t"
|
||||
|
||||
# Error handling rule
|
||||
|
||||
@@ -129,11 +131,11 @@ lexer = lex.lex()
|
||||
|
||||
|
||||
precedence = (
|
||||
('left', 'PLUS', 'MINUS'),
|
||||
('left', 'DIVIDE'),
|
||||
('left', 'TIMES'),
|
||||
('left', 'AND', 'OR'),
|
||||
('right', 'NEG'),
|
||||
("left", "PLUS", "MINUS"),
|
||||
("left", "DIVIDE"),
|
||||
("left", "TIMES"),
|
||||
("left", "AND", "OR"),
|
||||
("right", "NEG"),
|
||||
)
|
||||
|
||||
|
||||
@@ -146,152 +148,147 @@ def default_read_register(reg):
|
||||
|
||||
|
||||
def default_get_descriptor(selector):
|
||||
return (0, 0xfffff000, 'rwx')
|
||||
return (0, 0xFFFFF000, "rwx")
|
||||
|
||||
|
||||
default_sizes_32 = {'QWORD': 8,
|
||||
'DWORD': 4,
|
||||
'WORD': 2,
|
||||
'BYTE': 1}
|
||||
default_sizes_32 = {"QWORD": 8, "DWORD": 4, "WORD": 2, "BYTE": 1}
|
||||
|
||||
default_sizes_64 = {'QWORD': 8,
|
||||
'DWORD': 4,
|
||||
'WORD': 2,
|
||||
'BYTE': 1}
|
||||
functions = {'read_memory': default_read_memory,
|
||||
'read_register': default_read_register,
|
||||
'get_descriptor': default_get_descriptor,
|
||||
}
|
||||
default_sizes_64 = {"QWORD": 8, "DWORD": 4, "WORD": 2, "BYTE": 1}
|
||||
functions = {
|
||||
"read_memory": default_read_memory,
|
||||
"read_register": default_read_register,
|
||||
"get_descriptor": default_get_descriptor,
|
||||
}
|
||||
sizes = copy.copy(default_sizes_32)
|
||||
|
||||
|
||||
def p_expression_div(p):
|
||||
'expression : expression DIVIDE expression'
|
||||
"expression : expression DIVIDE expression"
|
||||
p[0] = p[1] // p[3]
|
||||
|
||||
|
||||
def p_expression_mul(p):
|
||||
'expression : expression TIMES expression'
|
||||
"expression : expression TIMES expression"
|
||||
p[0] = p[1] * p[3]
|
||||
|
||||
|
||||
def p_expression_plus(p):
|
||||
'expression : expression PLUS expression'
|
||||
"expression : expression PLUS expression"
|
||||
p[0] = p[1] + p[3]
|
||||
|
||||
|
||||
def p_expression_minus(p):
|
||||
'expression : expression MINUS expression'
|
||||
"expression : expression MINUS expression"
|
||||
p[0] = p[1] - p[3]
|
||||
|
||||
|
||||
def p_expression_and(p):
|
||||
'expression : expression AND expression'
|
||||
"expression : expression AND expression"
|
||||
p[0] = p[1] & p[3]
|
||||
|
||||
|
||||
def p_expression_or(p):
|
||||
'expression : expression OR expression'
|
||||
"expression : expression OR expression"
|
||||
p[0] = p[1] | p[3]
|
||||
|
||||
|
||||
def p_expression_neg(p):
|
||||
'expression : NEG expression '
|
||||
"expression : NEG expression "
|
||||
p[0] = ~p[1]
|
||||
|
||||
|
||||
def p_expression_lshift(p):
|
||||
'expression : expression LSHIFT expression'
|
||||
"expression : expression LSHIFT expression"
|
||||
p[0] = p[1] << p[3]
|
||||
|
||||
|
||||
def p_expression_rshift(p):
|
||||
'expression : expression RSHIFT expression'
|
||||
"expression : expression RSHIFT expression"
|
||||
p[0] = p[1] >> p[3]
|
||||
|
||||
|
||||
def p_expression_deref(p):
|
||||
'expression : TYPE PTR LBRAKET expression RBRAKET'
|
||||
"expression : TYPE PTR LBRAKET expression RBRAKET"
|
||||
size = sizes[p[1]]
|
||||
address = p[4]
|
||||
char_list = functions['read_memory'](address, size)
|
||||
char_list = functions["read_memory"](address, size)
|
||||
value = Operators.CONCAT(8 * len(char_list), *reversed(map(Operators.ORD, char_list)))
|
||||
p[0] = value
|
||||
|
||||
|
||||
def p_expression_derefseg(p):
|
||||
'expression : TYPE PTR SEGMENT COLOM LBRAKET expression RBRAKET'
|
||||
"expression : TYPE PTR SEGMENT COLOM LBRAKET expression RBRAKET"
|
||||
size = sizes[p[1]]
|
||||
address = p[6]
|
||||
seg = functions['read_register'](p[3])
|
||||
base, limit, _ = functions['get_descriptor'](seg)
|
||||
seg = functions["read_register"](p[3])
|
||||
base, limit, _ = functions["get_descriptor"](seg)
|
||||
address = base + address
|
||||
char_list = functions['read_memory'](address, size)
|
||||
char_list = functions["read_memory"](address, size)
|
||||
value = Operators.CONCAT(8 * len(char_list), *reversed(map(Operators.ORD, char_list)))
|
||||
p[0] = value
|
||||
|
||||
|
||||
def p_expression_term(p):
|
||||
'expression : term'
|
||||
"expression : term"
|
||||
p[0] = p[1]
|
||||
|
||||
|
||||
def p_factor_expr(p):
|
||||
'expression : LPAREN expression RPAREN'
|
||||
"expression : LPAREN expression RPAREN"
|
||||
p[0] = p[2]
|
||||
|
||||
|
||||
def p_term_num(p):
|
||||
'term : NUMBER'
|
||||
"term : NUMBER"
|
||||
p[0] = p[1]
|
||||
|
||||
|
||||
def p_term_reg(p):
|
||||
'term : REGISTER'
|
||||
p[0] = functions['read_register'](p[1])
|
||||
"term : REGISTER"
|
||||
p[0] = functions["read_register"](p[1])
|
||||
|
||||
|
||||
def p_expression_eq(p):
|
||||
'expression : expression EQ expression'
|
||||
"expression : expression EQ expression"
|
||||
p[0] = p[1] == p[3]
|
||||
|
||||
|
||||
def p_expression_land(p):
|
||||
'expression : expression LAND expression'
|
||||
"expression : expression LAND expression"
|
||||
p[0] = p[1] and p[3]
|
||||
|
||||
|
||||
def p_expression_lor(p):
|
||||
'expression : expression LOR expression'
|
||||
"expression : expression LOR expression"
|
||||
p[0] = p[1] or p[3]
|
||||
|
||||
|
||||
def p_expression_lnot(p):
|
||||
'expression : LNOT expression'
|
||||
"expression : LNOT expression"
|
||||
p[0] = not p[1]
|
||||
|
||||
|
||||
def p_expression_lt(p):
|
||||
'expression : expression LT expression'
|
||||
#p[0] = p[1] < p[3]
|
||||
"expression : expression LT expression"
|
||||
# p[0] = p[1] < p[3]
|
||||
p[0] = Operators.ULT(p[1], p[3])
|
||||
|
||||
|
||||
def p_expression_le(p):
|
||||
'expression : expression LE expression'
|
||||
#p[0] = p[1] <= p[3]
|
||||
"expression : expression LE expression"
|
||||
# p[0] = p[1] <= p[3]
|
||||
p[0] = Operators.ULE(p[1], p[3])
|
||||
|
||||
|
||||
def p_expression_gt(p):
|
||||
'expression : expression GT expression'
|
||||
#p[0] = p[1] > p[3]
|
||||
"expression : expression GT expression"
|
||||
# p[0] = p[1] > p[3]
|
||||
p[0] = Operators.UGT(p[1], p[3])
|
||||
|
||||
|
||||
def p_expression_ge(p):
|
||||
'expression : expression GE expression'
|
||||
#p[0] = p[1] >= p[3]
|
||||
"expression : expression GE expression"
|
||||
# p[0] = p[1] >= p[3]
|
||||
p[0] = Operators.UGE(p[1], p[3])
|
||||
|
||||
|
||||
@@ -299,6 +296,7 @@ def p_expression_ge(p):
|
||||
def p_error(p):
|
||||
print("Syntax error in input:", p)
|
||||
|
||||
|
||||
# Build the parser
|
||||
|
||||
|
||||
@@ -309,19 +307,19 @@ def parse(expression, read_memory=None, read_register=None, get_descriptor=None,
|
||||
global functions, sizes
|
||||
|
||||
if read_memory is not None:
|
||||
functions['read_memory'] = read_memory
|
||||
functions["read_memory"] = read_memory
|
||||
else:
|
||||
functions['read_memory'] = default_read_memory
|
||||
functions["read_memory"] = default_read_memory
|
||||
|
||||
if read_register is not None:
|
||||
functions['read_register'] = read_register
|
||||
functions["read_register"] = read_register
|
||||
else:
|
||||
functions['read_register'] = default_read_register
|
||||
functions["read_register"] = default_read_register
|
||||
|
||||
if get_descriptor is not None:
|
||||
functions['get_descriptor'] = get_descriptor
|
||||
functions["get_descriptor"] = get_descriptor
|
||||
else:
|
||||
functions['get_descriptor'] = default_get_descriptor
|
||||
functions["get_descriptor"] = default_get_descriptor
|
||||
|
||||
if word_size == 32:
|
||||
sizes = copy.copy(default_sizes_32)
|
||||
@@ -330,17 +328,17 @@ def parse(expression, read_memory=None, read_register=None, get_descriptor=None,
|
||||
else:
|
||||
raise Exception("Not Supported")
|
||||
result = parser.parse(expression, tracking=True)
|
||||
del functions['read_memory']
|
||||
del functions['read_register']
|
||||
del functions['get_descriptor']
|
||||
del functions["read_memory"]
|
||||
del functions["read_register"]
|
||||
del functions["get_descriptor"]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
while True:
|
||||
try:
|
||||
s = input('calc > ')
|
||||
s = input("calc > ")
|
||||
except EOFError:
|
||||
break
|
||||
if not s:
|
||||
|
||||
+63
-69
@@ -49,8 +49,8 @@ class Plugin:
|
||||
pass
|
||||
|
||||
def generate_testcase(self, state, testcase, message):
|
||||
''' Called so the plugin can attach some results to the testcase if the
|
||||
state needs it'''
|
||||
""" Called so the plugin can attach some results to the testcase if the
|
||||
state needs it"""
|
||||
pass
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ def _dict_diff(d1, d2):
|
||||
|
||||
class Tracer(Plugin):
|
||||
def did_execute_instruction_callback(self, state, pc, target_pc, instruction):
|
||||
state.context.setdefault('trace', []).append(pc)
|
||||
state.context.setdefault("trace", []).append(pc)
|
||||
|
||||
|
||||
class ExtendedTracer(Plugin):
|
||||
@@ -85,7 +85,7 @@ class ExtendedTracer(Plugin):
|
||||
super().__init__()
|
||||
self.last_dict = {}
|
||||
self.current_pc = None
|
||||
self.context_key = 'e_trace'
|
||||
self.context_key = "e_trace"
|
||||
|
||||
def get_trace(self, state):
|
||||
return state.context.get(self.context_key)
|
||||
@@ -94,7 +94,7 @@ class ExtendedTracer(Plugin):
|
||||
d = {}
|
||||
for reg in cpu.canonical_registers:
|
||||
val = cpu.read_register(reg)
|
||||
d[reg] = val if not issymbolic(val) else '<sym>'
|
||||
d[reg] = val if not issymbolic(val) else "<sym>"
|
||||
return d
|
||||
|
||||
def will_execute_instruction_callback(self, state, pc, instruction):
|
||||
@@ -102,10 +102,7 @@ class ExtendedTracer(Plugin):
|
||||
|
||||
def did_execute_instruction_callback(self, state, pc, target_pc, instruction):
|
||||
reg_state = self.register_state_to_dict(state.cpu)
|
||||
entry = {
|
||||
'type': 'regs',
|
||||
'values': _dict_diff(self.last_dict, reg_state)
|
||||
}
|
||||
entry = {"type": "regs", "values": _dict_diff(self.last_dict, reg_state)}
|
||||
self.last_dict = reg_state
|
||||
state.context.setdefault(self.context_key, []).append(entry)
|
||||
|
||||
@@ -132,12 +129,7 @@ class ExtendedTracer(Plugin):
|
||||
raise Exception
|
||||
return
|
||||
|
||||
entry = {
|
||||
'type': 'mem_write',
|
||||
'where': where,
|
||||
'value': value,
|
||||
'size': size
|
||||
}
|
||||
entry = {"type": "mem_write", "where": where, "value": value, "size": size}
|
||||
state.context.setdefault(self.context_key, []).append(entry)
|
||||
|
||||
|
||||
@@ -155,28 +147,28 @@ class Follower(Plugin):
|
||||
|
||||
def get_next(self, type):
|
||||
event = self.trace[self.index]
|
||||
assert event['type'] == type
|
||||
assert event["type"] == type
|
||||
self.index += 1
|
||||
return event
|
||||
|
||||
def did_write_memory_callback(self, state, where, value, size):
|
||||
if not self.active:
|
||||
return
|
||||
write = self.get_next('mem_write')
|
||||
write = self.get_next("mem_write")
|
||||
|
||||
if not issymbolic(value):
|
||||
return
|
||||
|
||||
assert write['where'] == where and write['size'] == size
|
||||
assert write["where"] == where and write["size"] == size
|
||||
# state.constrain(value == write['value'])
|
||||
|
||||
def did_execute_instruction_callback(self, state, last_pc, pc, insn):
|
||||
if not self.active:
|
||||
return
|
||||
event = self.get_next('regs')
|
||||
self.last_instruction = event['values']
|
||||
event = self.get_next("regs")
|
||||
self.last_instruction = event["values"]
|
||||
if issymbolic(pc):
|
||||
state.constrain(state.cpu.RIP == self.last_instruction['RIP'])
|
||||
state.constrain(state.cpu.RIP == self.last_instruction["RIP"])
|
||||
else:
|
||||
for start, stop in self.symbolic_ranges:
|
||||
if start <= pc <= stop:
|
||||
@@ -185,75 +177,76 @@ class Follower(Plugin):
|
||||
|
||||
class RecordSymbolicBranches(Plugin):
|
||||
def did_execute_instruction_callback(self, state, last_pc, target_pc, instruction):
|
||||
if state.context.get('forking_pc', False):
|
||||
branches = state.context.setdefault('branches', {})
|
||||
if state.context.get("forking_pc", False):
|
||||
branches = state.context.setdefault("branches", {})
|
||||
branch = (last_pc, target_pc)
|
||||
if branch in branches:
|
||||
branches[branch] += 1
|
||||
else:
|
||||
branches[branch] = 1
|
||||
state.context['forking_pc'] = False
|
||||
state.context["forking_pc"] = False
|
||||
|
||||
if issymbolic(target_pc):
|
||||
state.context['forking_pc'] = True
|
||||
state.context["forking_pc"] = True
|
||||
|
||||
|
||||
class InstructionCounter(Plugin):
|
||||
|
||||
def will_terminate_state_callback(self, state, ex):
|
||||
if state is None: # FIXME Can it be None?
|
||||
return
|
||||
state_instructions_count = state.context.get('instructions_count', 0)
|
||||
state_instructions_count = state.context.get("instructions_count", 0)
|
||||
|
||||
with self.manticore.locked_context() as manticore_context:
|
||||
manticore_instructions_count = manticore_context.get('instructions_count', 0)
|
||||
manticore_context['instructions_count'] = manticore_instructions_count + state_instructions_count
|
||||
manticore_instructions_count = manticore_context.get("instructions_count", 0)
|
||||
manticore_context["instructions_count"] = (
|
||||
manticore_instructions_count + state_instructions_count
|
||||
)
|
||||
|
||||
def did_execute_instruction_callback(self, state, prev_pc, target_pc, instruction):
|
||||
address = prev_pc
|
||||
if not issymbolic(address):
|
||||
count = state.context.get('instructions_count', 0)
|
||||
state.context['instructions_count'] = count + 1
|
||||
count = state.context.get("instructions_count", 0)
|
||||
state.context["instructions_count"] = count + 1
|
||||
|
||||
def did_run_callback(self):
|
||||
_shared_context = self.manticore.context
|
||||
instructions_count = _shared_context.get('instructions_count', 0)
|
||||
logger.info('Instructions executed: %d', instructions_count)
|
||||
instructions_count = _shared_context.get("instructions_count", 0)
|
||||
logger.info("Instructions executed: %d", instructions_count)
|
||||
|
||||
|
||||
class Visited(Plugin):
|
||||
def __init__(self, coverage_file='visited.txt'):
|
||||
def __init__(self, coverage_file="visited.txt"):
|
||||
super().__init__()
|
||||
self.coverage_file = coverage_file
|
||||
|
||||
def will_terminate_state_callback(self, state, ex):
|
||||
if state is None:
|
||||
return
|
||||
state_visited = state.context.get('visited_since_last_fork', set())
|
||||
state_visited = state.context.get("visited_since_last_fork", set())
|
||||
with self.manticore.locked_context() as manticore_context:
|
||||
manticore_visited = manticore_context.get('visited', set())
|
||||
manticore_context['visited'] = manticore_visited.union(state_visited)
|
||||
manticore_visited = manticore_context.get("visited", set())
|
||||
manticore_context["visited"] = manticore_visited.union(state_visited)
|
||||
|
||||
def will_fork_state_callback(self, state, expression, values, policy):
|
||||
state_visited = state.context.get('visited_since_last_fork', set())
|
||||
state_visited = state.context.get("visited_since_last_fork", set())
|
||||
with self.manticore.locked_context() as manticore_context:
|
||||
manticore_visited = manticore_context.get('visited', set())
|
||||
manticore_context['visited'] = manticore_visited.union(state_visited)
|
||||
state.context['visited_since_last_fork'] = set()
|
||||
manticore_visited = manticore_context.get("visited", set())
|
||||
manticore_context["visited"] = manticore_visited.union(state_visited)
|
||||
state.context["visited_since_last_fork"] = set()
|
||||
|
||||
def did_execute_instruction_callback(self, state, prev_pc, target_pc, instruction):
|
||||
state.context.setdefault('visited_since_last_fork', set()).add(prev_pc)
|
||||
state.context.setdefault('visited', set()).add(prev_pc)
|
||||
state.context.setdefault("visited_since_last_fork", set()).add(prev_pc)
|
||||
state.context.setdefault("visited", set()).add(prev_pc)
|
||||
|
||||
def did_run_callback(self):
|
||||
_shared_context = self.manticore.context
|
||||
executor_visited = _shared_context.get('visited', set())
|
||||
executor_visited = _shared_context.get("visited", set())
|
||||
# Fixme this is duplicated?
|
||||
if self.coverage_file is not None:
|
||||
with self.manticore._output.save_stream(self.coverage_file) as f:
|
||||
for m in executor_visited:
|
||||
f.write(f"0x{m:016x}\n")
|
||||
logger.info('Coverage: %d different instructions executed', len(executor_visited))
|
||||
logger.info("Coverage: %d different instructions executed", len(executor_visited))
|
||||
|
||||
|
||||
class Profiler(Plugin):
|
||||
@@ -266,7 +259,7 @@ class Profiler(Plugin):
|
||||
def did_terminate_worker_callback(self, id):
|
||||
self.data.profile.disable()
|
||||
self.data.profile.create_stats()
|
||||
with self.manticore.locked_context('_profiling_stats', dict) as profiling_stats:
|
||||
with self.manticore.locked_context("_profiling_stats", dict) as profiling_stats:
|
||||
profiling_stats[id] = self.data.profile.stats.items()
|
||||
|
||||
def get_profiling_data(self):
|
||||
@@ -277,7 +270,7 @@ class Profiler(Plugin):
|
||||
def create_stats(self):
|
||||
pass
|
||||
|
||||
with self.manticore.locked_context('_profiling_stats') as profiling_stats:
|
||||
with self.manticore.locked_context("_profiling_stats") as profiling_stats:
|
||||
ps = None
|
||||
for item in profiling_stats.values():
|
||||
try:
|
||||
@@ -291,80 +284,81 @@ class Profiler(Plugin):
|
||||
return ps
|
||||
|
||||
def save_profiling_data(self, stream=None):
|
||||
''':param stream: an output stream to write the profiling data '''
|
||||
""":param stream: an output stream to write the profiling data """
|
||||
ps = self.get_profiling_data()
|
||||
# XXX(yan): pstats does not support dumping to a file stream, only to a file
|
||||
# name. Below is essentially the implementation of pstats.dump_stats() without
|
||||
# the extra open().
|
||||
if stream is not None:
|
||||
import marshal
|
||||
|
||||
marshal.dump(ps.stats, stream)
|
||||
|
||||
|
||||
# TODO document all callbacks
|
||||
class ExamplePlugin(Plugin):
|
||||
def will_open_transaction_callback(self, state, tx):
|
||||
logger.info('will open a transaction %r %r', state, tx)
|
||||
logger.info("will open a transaction %r %r", state, tx)
|
||||
|
||||
def will_close_transaction_callback(self, state, tx):
|
||||
logger.info('will close a transaction %r %r', state, tx)
|
||||
logger.info("will close a transaction %r %r", state, tx)
|
||||
|
||||
def will_decode_instruction_callback(self, state, pc):
|
||||
logger.info('will_decode_instruction %r %r', state, pc)
|
||||
logger.info("will_decode_instruction %r %r", state, pc)
|
||||
|
||||
def will_execute_instruction_callback(self, state, pc, instruction):
|
||||
logger.info('will_execute_instruction %r %r %r', state, pc, instruction)
|
||||
logger.info("will_execute_instruction %r %r %r", state, pc, instruction)
|
||||
|
||||
def did_execute_instruction_callback(self, state, pc, target_pc, instruction):
|
||||
logger.info('did_execute_instruction %r %r %r %r', state, pc, target_pc, instruction)
|
||||
logger.info("did_execute_instruction %r %r %r %r", state, pc, target_pc, instruction)
|
||||
|
||||
def will_start_run_callback(self, state):
|
||||
""" Called once at the beginning of the run.
|
||||
state is the initial root state
|
||||
"""
|
||||
logger.info('will_start_run')
|
||||
logger.info("will_start_run")
|
||||
|
||||
def did_run_callback(self):
|
||||
logger.info('did_run')
|
||||
logger.info("did_run")
|
||||
|
||||
def will_fork_state_callback(self, parent_state, expression, solutions, policy):
|
||||
logger.info('will_fork_state %r %r %r %r', parent_state, expression, solutions, policy)
|
||||
logger.info("will_fork_state %r %r %r %r", parent_state, expression, solutions, policy)
|
||||
|
||||
def did_fork_state_callback(self, child_state, expression, new_value, policy):
|
||||
logger.info('did_fork_state %r %r %r %r', child_state, expression, new_value, policy)
|
||||
logger.info("did_fork_state %r %r %r %r", child_state, expression, new_value, policy)
|
||||
|
||||
def did_load_state_callback(self, state, state_id):
|
||||
logger.info('did_load_state %r %r', state, state_id)
|
||||
logger.info("did_load_state %r %r", state, state_id)
|
||||
|
||||
def did_enqueue_state_callback(self, state, state_id):
|
||||
logger.info('did_enqueue_state %r %r', state, state_id)
|
||||
logger.info("did_enqueue_state %r %r", state, state_id)
|
||||
|
||||
def will_terminate_state_callback(self, state, exception):
|
||||
logger.info('will_terminate_state %r %r', state, exception)
|
||||
logger.info("will_terminate_state %r %r", state, exception)
|
||||
|
||||
def will_generate_testcase_callback(self, state, testcase, message):
|
||||
logger.info('will_generate_testcase %r %r %r', state, testcase, message)
|
||||
logger.info("will_generate_testcase %r %r %r", state, testcase, message)
|
||||
|
||||
def will_read_memory_callback(self, state, where, size):
|
||||
logger.info('will_read_memory %r %r %r', state, where, size)
|
||||
logger.info("will_read_memory %r %r %r", state, where, size)
|
||||
|
||||
def did_read_memory_callback(self, state, where, value, size):
|
||||
logger.info('did_read_memory %r %r %r %r', state, where, value, size)
|
||||
logger.info("did_read_memory %r %r %r %r", state, where, value, size)
|
||||
|
||||
def will_write_memory_callback(self, state, where, value, size):
|
||||
logger.info('will_write_memory %r %r %r', state, where, value, size)
|
||||
logger.info("will_write_memory %r %r %r", state, where, value, size)
|
||||
|
||||
def did_write_memory_callback(self, state, where, value, size):
|
||||
logger.info('did_write_memory %r %r %r %r', state, where, value, size)
|
||||
logger.info("did_write_memory %r %r %r %r", state, where, value, size)
|
||||
|
||||
def will_read_register_callback(self, state, register):
|
||||
logger.info('will_read_register %r %r', state, register)
|
||||
logger.info("will_read_register %r %r", state, register)
|
||||
|
||||
def did_read_register_callback(self, state, register, value):
|
||||
logger.info('did_read_register %r %r %r', state, register, value)
|
||||
logger.info("did_read_register %r %r %r", state, register, value)
|
||||
|
||||
def will_write_register_callback(self, state, register, value):
|
||||
logger.info('will_write_register %r %r %r', state, register, value)
|
||||
logger.info("will_write_register %r %r %r", state, register, value)
|
||||
|
||||
def did_write_register_callback(self, state, register, value):
|
||||
logger.info('did_write_register %r %r %r', state, register, value)
|
||||
logger.info("did_write_register %r %r %r", state, register, value)
|
||||
|
||||
@@ -4,4 +4,5 @@ from .solver import * # noqa
|
||||
from . import operators as Operators # noqa
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -2,7 +2,19 @@ import itertools
|
||||
import sys
|
||||
|
||||
from ...utils.helpers import PickleSerializer
|
||||
from .expression import BitVecVariable, BoolVariable, ArrayVariable, Array, Bool, BitVec, BoolConstant, ArrayProxy, BoolEq, Variable, Constant
|
||||
from .expression import (
|
||||
BitVecVariable,
|
||||
BoolVariable,
|
||||
ArrayVariable,
|
||||
Array,
|
||||
Bool,
|
||||
BitVec,
|
||||
BoolConstant,
|
||||
ArrayProxy,
|
||||
BoolEq,
|
||||
Variable,
|
||||
Constant,
|
||||
)
|
||||
from .visitors import GetDeclarations, TranslatorSmtlib, get_variables, simplify, replace
|
||||
import logging
|
||||
|
||||
@@ -24,7 +36,16 @@ class ConstraintSet:
|
||||
self._child = None
|
||||
|
||||
def __reduce__(self):
|
||||
return (self.__class__, (), {'_parent': self._parent, '_constraints': self._constraints, '_sid': self._sid, '_declarations': self._declarations})
|
||||
return (
|
||||
self.__class__,
|
||||
(),
|
||||
{
|
||||
"_parent": self._parent,
|
||||
"_constraints": self._constraints,
|
||||
"_sid": self._sid,
|
||||
"_declarations": self._declarations,
|
||||
},
|
||||
)
|
||||
|
||||
def __enter__(self):
|
||||
assert self._child is None
|
||||
@@ -60,7 +81,7 @@ class ConstraintSet:
|
||||
# constraints to this one. After the child constraintSet is deleted
|
||||
# we regain the ability to add constraints.
|
||||
if self._child is not None:
|
||||
raise Exception('ConstraintSet is frozen')
|
||||
raise Exception("ConstraintSet is frozen")
|
||||
|
||||
if isinstance(constraint, BoolConstant):
|
||||
if not constraint.value:
|
||||
@@ -73,6 +94,7 @@ class ConstraintSet:
|
||||
|
||||
if check:
|
||||
from ...core.smtlib import solver
|
||||
|
||||
if not solver.check(self):
|
||||
raise ValueError("Added an impossible constraint")
|
||||
|
||||
@@ -92,7 +114,7 @@ class ConstraintSet:
|
||||
added = True
|
||||
while added:
|
||||
added = False
|
||||
logger.debug('Related variables %r', [x.name for x in related_variables])
|
||||
logger.debug("Related variables %r", [x.name for x in related_variables])
|
||||
for constraint in list(remaining_constraints):
|
||||
if isinstance(constraint, BoolConstant):
|
||||
if constraint.value:
|
||||
@@ -108,7 +130,9 @@ class ConstraintSet:
|
||||
related_variables |= variables
|
||||
added = True
|
||||
|
||||
logger.debug('Reduced %d constraints!!', number_of_constraints - len(related_constraints))
|
||||
logger.debug(
|
||||
"Reduced %d constraints!!", number_of_constraints - len(related_constraints)
|
||||
)
|
||||
else:
|
||||
related_variables = set()
|
||||
for constraint in self.constraints:
|
||||
@@ -122,13 +146,15 @@ class ConstraintSet:
|
||||
if replace_constants:
|
||||
constant_bindings = {}
|
||||
for expression in self.constraints:
|
||||
if isinstance(expression, BoolEq) and \
|
||||
isinstance(expression.operands[0], Variable) and \
|
||||
isinstance(expression.operands[1], Constant):
|
||||
if (
|
||||
isinstance(expression, BoolEq)
|
||||
and isinstance(expression.operands[0], Variable)
|
||||
and isinstance(expression.operands[1], Constant)
|
||||
):
|
||||
constant_bindings[expression.operands[0]] = expression.operands[1]
|
||||
|
||||
tmp = set()
|
||||
result = ''
|
||||
result = ""
|
||||
for var in related_variables:
|
||||
# FIXME
|
||||
# band aid hack around the fact that we are double declaring stuff :( :(
|
||||
@@ -136,14 +162,16 @@ class ConstraintSet:
|
||||
logger.warning("Variable '%s' was copied twice somewhere", var.name)
|
||||
continue
|
||||
tmp.add(var.declaration)
|
||||
result += var.declaration + '\n'
|
||||
result += var.declaration + "\n"
|
||||
|
||||
translator = TranslatorSmtlib(use_bindings=True)
|
||||
for constraint in related_constraints:
|
||||
if replace_constants:
|
||||
if isinstance(constraint, BoolEq) and \
|
||||
isinstance(constraint.operands[0], Variable) and \
|
||||
isinstance(constraint.operands[1], Constant):
|
||||
if (
|
||||
isinstance(constraint, BoolEq)
|
||||
and isinstance(constraint.operands[0], Variable)
|
||||
and isinstance(constraint.operands[1], Constant)
|
||||
):
|
||||
var = constraint.operands[0]
|
||||
expression = constraint.operands[1]
|
||||
expression = simplify(replace(expression, constant_bindings))
|
||||
@@ -152,26 +180,26 @@ class ConstraintSet:
|
||||
translator.visit(constraint)
|
||||
for name, exp, smtlib in translator.bindings:
|
||||
if isinstance(exp, BitVec):
|
||||
result += f'(declare-fun {name} () (_ BitVec {exp.size}))'
|
||||
result += f"(declare-fun {name} () (_ BitVec {exp.size}))"
|
||||
elif isinstance(exp, Bool):
|
||||
result += f'(declare-fun {name} () Bool)'
|
||||
result += f"(declare-fun {name} () Bool)"
|
||||
elif isinstance(exp, Array):
|
||||
result += f'(declare-fun {name} () (Array (_ BitVec {exp.index_bits}) (_ BitVec {exp.value_bits})))'
|
||||
result += f"(declare-fun {name} () (Array (_ BitVec {exp.index_bits}) (_ BitVec {exp.value_bits})))"
|
||||
else:
|
||||
raise Exception(f"Type not supported {exp!r}")
|
||||
result += f'(assert (= {name} {smtlib}))\n'
|
||||
result += f"(assert (= {name} {smtlib}))\n"
|
||||
|
||||
constraint_str = translator.pop()
|
||||
while constraint_str is not None:
|
||||
if constraint_str != 'true':
|
||||
result += f'(assert {constraint_str})\n'
|
||||
if constraint_str != "true":
|
||||
result += f"(assert {constraint_str})\n"
|
||||
constraint_str = translator.pop()
|
||||
return result
|
||||
|
||||
def _declare(self, var):
|
||||
""" Declare the variable `var` """
|
||||
if var.name in self._declarations:
|
||||
raise ValueError('Variable already declared')
|
||||
raise ValueError("Variable already declared")
|
||||
self._declarations[var.name] = var
|
||||
return var
|
||||
|
||||
@@ -193,7 +221,9 @@ class ConstraintSet:
|
||||
except RuntimeError:
|
||||
# TODO: (defunct) move recursion management out of PickleSerializer
|
||||
if sys.getrecursionlimit() >= PickleSerializer.MAX_RECURSION:
|
||||
raise Exception(f'declarations recursion limit surpassed {PickleSerializer.MAX_RECURSION}, aborting')
|
||||
raise Exception(
|
||||
f"declarations recursion limit surpassed {PickleSerializer.MAX_RECURSION}, aborting"
|
||||
)
|
||||
new_limit = sys.getrecursionlimit() + PickleSerializer.DEFAULT_RECURSION
|
||||
if new_limit <= PickleSerializer.DEFAULT_RECURSION:
|
||||
sys.setrecursionlimit(new_limit)
|
||||
@@ -217,19 +247,19 @@ class ConstraintSet:
|
||||
""" Returns a smtlib representation of the current state """
|
||||
return self.to_string()
|
||||
|
||||
def _make_unique_name(self, name='VAR'):
|
||||
def _make_unique_name(self, name="VAR"):
|
||||
""" Makes a unique variable name"""
|
||||
# the while loop is necessary because appending the result of _get_sid()
|
||||
# is not guaranteed to make a unique name on the first try; a colliding
|
||||
# name could have been added previously
|
||||
while name in self._declarations:
|
||||
name = f'{name}_{self._get_sid()}'
|
||||
name = f"{name}_{self._get_sid()}"
|
||||
return name
|
||||
|
||||
def is_declared(self, expression_var):
|
||||
""" True if expression_var is declared in this constraint set """
|
||||
if not isinstance(expression_var, Variable):
|
||||
raise ValueError(f'Expression must be a Variable (not a {type(expression_var)})')
|
||||
raise ValueError(f"Expression must be a Variable (not a {type(expression_var)})")
|
||||
return any(expression_var is x for x in self.get_declared_variables())
|
||||
|
||||
def migrate(self, expression, name_migration_map=None):
|
||||
@@ -257,14 +287,16 @@ class ConstraintSet:
|
||||
# expressions, and its values should ALWAYS be internal/local expressions
|
||||
object_migration_map = {}
|
||||
|
||||
#List of foreign vars used in expression
|
||||
# List of foreign vars used in expression
|
||||
foreign_vars = itertools.filterfalse(self.is_declared, get_variables(expression))
|
||||
for foreign_var in foreign_vars:
|
||||
# If a variable with the same name was previously migrated
|
||||
if foreign_var.name in name_migration_map:
|
||||
migrated_name = name_migration_map[foreign_var.name]
|
||||
native_var = self.get_variable(migrated_name)
|
||||
assert native_var is not None, "name_migration_map contains a variable that does not exist in this ConstraintSet"
|
||||
assert (
|
||||
native_var is not None
|
||||
), "name_migration_map contains a variable that does not exist in this ConstraintSet"
|
||||
object_migration_map[foreign_var] = native_var
|
||||
else:
|
||||
# foreign_var was not found in the local declared variables nor
|
||||
@@ -272,7 +304,7 @@ class ConstraintSet:
|
||||
# let's make a new unique internal name for it
|
||||
migrated_name = foreign_var.name
|
||||
if migrated_name in self._declarations:
|
||||
migrated_name = self._make_unique_name(f'{foreign_var.name}_migrated')
|
||||
migrated_name = self._make_unique_name(f"{foreign_var.name}_migrated")
|
||||
# Create and declare a new variable of given type
|
||||
if isinstance(foreign_var, Bool):
|
||||
new_var = self.new_bool(name=migrated_name)
|
||||
@@ -280,9 +312,16 @@ class ConstraintSet:
|
||||
new_var = self.new_bitvec(foreign_var.size, name=migrated_name)
|
||||
elif isinstance(foreign_var, Array):
|
||||
# Note that we are discarding the ArrayProxy encapsulation
|
||||
new_var = self.new_array(index_max=foreign_var.index_max, index_bits=foreign_var.index_bits, value_bits=foreign_var.value_bits, name=migrated_name).array
|
||||
new_var = self.new_array(
|
||||
index_max=foreign_var.index_max,
|
||||
index_bits=foreign_var.index_bits,
|
||||
value_bits=foreign_var.value_bits,
|
||||
name=migrated_name,
|
||||
).array
|
||||
else:
|
||||
raise NotImplemented(f"Unknown expression type {type(var)} encountered during expression migration")
|
||||
raise NotImplemented(
|
||||
f"Unknown expression type {type(var)} encountered during expression migration"
|
||||
)
|
||||
# Update the var to var mapping
|
||||
object_migration_map[foreign_var] = new_var
|
||||
# Update the name to name mapping
|
||||
@@ -300,12 +339,12 @@ class ConstraintSet:
|
||||
:return: a fresh BoolVariable
|
||||
"""
|
||||
if name is None:
|
||||
name = 'B'
|
||||
name = "B"
|
||||
avoid_collisions = True
|
||||
if avoid_collisions:
|
||||
name = self._make_unique_name(name)
|
||||
if not avoid_collisions and name in self._declarations:
|
||||
raise ValueError(f'Name {name} already used')
|
||||
raise ValueError(f"Name {name} already used")
|
||||
var = BoolVariable(name, taint=taint)
|
||||
return self._declare(var)
|
||||
|
||||
@@ -318,18 +357,27 @@ class ConstraintSet:
|
||||
:return: a fresh BitVecVariable
|
||||
"""
|
||||
if not (size == 1 or size % 8 == 0):
|
||||
raise Exception(f'Invalid bitvec size {size}')
|
||||
raise Exception(f"Invalid bitvec size {size}")
|
||||
if name is None:
|
||||
name = 'BV'
|
||||
name = "BV"
|
||||
avoid_collisions = True
|
||||
if avoid_collisions:
|
||||
name = self._make_unique_name(name)
|
||||
if not avoid_collisions and name in self._declarations:
|
||||
raise ValueError(f'Name {name} already used')
|
||||
raise ValueError(f"Name {name} already used")
|
||||
var = BitVecVariable(size, name, taint=taint)
|
||||
return self._declare(var)
|
||||
|
||||
def new_array(self, index_bits=32, name=None, index_max=None, value_bits=8, taint=frozenset(), avoid_collisions=False, default=None):
|
||||
def new_array(
|
||||
self,
|
||||
index_bits=32,
|
||||
name=None,
|
||||
index_max=None,
|
||||
value_bits=8,
|
||||
taint=frozenset(),
|
||||
avoid_collisions=False,
|
||||
default=None,
|
||||
):
|
||||
""" Declares a free symbolic array of value_bits long bitvectors in the constraint store.
|
||||
:param index_bits: size in bits for the array indexes one of [32, 64]
|
||||
:param value_bits: size in bits for the array values
|
||||
@@ -341,11 +389,11 @@ class ConstraintSet:
|
||||
:return: a fresh ArrayProxy
|
||||
"""
|
||||
if name is None:
|
||||
name = 'A'
|
||||
name = "A"
|
||||
avoid_collisions = True
|
||||
if avoid_collisions:
|
||||
name = self._make_unique_name(name)
|
||||
if not avoid_collisions and name in self._declarations:
|
||||
raise ValueError(f'Name {name} already used')
|
||||
raise ValueError(f"Name {name} already used")
|
||||
var = self._declare(ArrayVariable(index_bits, index_max, value_bits, name, taint=taint))
|
||||
return ArrayProxy(var, default=default)
|
||||
|
||||
@@ -13,7 +13,7 @@ class Expression:
|
||||
self._taint = frozenset(taint)
|
||||
|
||||
def __repr__(self):
|
||||
return '<{:s} at {:x}{:s}>'.format(type(self).__name__, id(self), self.taint and '-T' or '')
|
||||
return "<{:s} at {:x}{:s}>".format(type(self).__name__, id(self), self.taint and "-T" or "")
|
||||
|
||||
@property
|
||||
def is_tainted(self):
|
||||
@@ -28,7 +28,7 @@ class Variable(Expression):
|
||||
def __init__(self, name, *args, **kwargs):
|
||||
if self.__class__ is Variable:
|
||||
raise TypeError
|
||||
assert isinstance(name, str) and ' ' not in name
|
||||
assert isinstance(name, str) and " " not in name
|
||||
super().__init__(*args, **kwargs)
|
||||
self._name = name
|
||||
|
||||
@@ -47,7 +47,7 @@ class Variable(Expression):
|
||||
raise Exception("Copying of Variables is not allowed.")
|
||||
|
||||
def __repr__(self):
|
||||
return '<{:s}({:s}) at {:x}>'.format(type(self).__name__, self.name, id(self))
|
||||
return "<{:s}({:s}) at {:x}>".format(type(self).__name__, self.name, id(self))
|
||||
|
||||
|
||||
class Constant(Expression):
|
||||
@@ -67,13 +67,13 @@ class Operation(Expression):
|
||||
def __init__(self, *operands, **kwargs):
|
||||
if self.__class__ is Operation:
|
||||
raise TypeError
|
||||
#assert len(operands) > 0
|
||||
#assert all(isinstance(x, Expression) for x in operands)
|
||||
# assert len(operands) > 0
|
||||
# assert all(isinstance(x, Expression) for x in operands)
|
||||
self._operands = operands
|
||||
|
||||
# If taint was not forced by a keyword argument, calculate default
|
||||
if 'taint' not in kwargs:
|
||||
kwargs['taint'] = reduce(lambda x, y: x.union(y.taint), operands, frozenset())
|
||||
if "taint" not in kwargs:
|
||||
kwargs["taint"] = reduce(lambda x, y: x.union(y.taint), operands, frozenset())
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@@ -137,7 +137,7 @@ class BoolVariable(Bool, Variable):
|
||||
|
||||
@property
|
||||
def declaration(self):
|
||||
return f'(declare-fun {self.name} () Bool)'
|
||||
return f"(declare-fun {self.name} () Bool)"
|
||||
|
||||
|
||||
class BoolConstant(Bool, Constant):
|
||||
@@ -390,7 +390,7 @@ class BitVecVariable(BitVec, Variable):
|
||||
|
||||
@property
|
||||
def declaration(self):
|
||||
return f'(declare-fun {self.name} () (_ BitVec {self.size}))'
|
||||
return f"(declare-fun {self.name} () (_ BitVec {self.size}))"
|
||||
|
||||
|
||||
class BitVecConstant(BitVec, Constant):
|
||||
@@ -553,8 +553,7 @@ class UnsignedGreaterThan(BoolOperation):
|
||||
class UnsignedGreaterOrEqual(BoolOperation):
|
||||
def __init__(self, a, b, *args, **kwargs):
|
||||
assert a.size == b.size
|
||||
super(UnsignedGreaterOrEqual,
|
||||
self).__init__(a, b, *args, **kwargs)
|
||||
super(UnsignedGreaterOrEqual, self).__init__(a, b, *args, **kwargs)
|
||||
|
||||
|
||||
###############################################################################
|
||||
@@ -569,13 +568,14 @@ class Array(Expression):
|
||||
self._index_max = index_max
|
||||
self._value_bits = value_bits
|
||||
super().__init__(*operands, **kwargs)
|
||||
assert type(self) is not Array, 'Abstract class'
|
||||
assert type(self) is not Array, "Abstract class"
|
||||
|
||||
def _get_size(self, index):
|
||||
start, stop = self._fix_index(index)
|
||||
size = stop - start
|
||||
if isinstance(size, BitVec):
|
||||
from .visitors import simplify
|
||||
|
||||
size = simplify(size)
|
||||
else:
|
||||
size = BitVecConstant(self.index_bits, size)
|
||||
@@ -604,7 +604,7 @@ class Array(Expression):
|
||||
|
||||
def cast_index(self, index):
|
||||
if isinstance(index, int):
|
||||
#assert self.index_max is None or index >= 0 and index < self.index_max
|
||||
# assert self.index_max is None or index >= 0 and index < self.index_max
|
||||
return BitVecConstant(self.index_bits, index)
|
||||
assert isinstance(index, BitVec) and index.size == self.index_bits
|
||||
return index
|
||||
@@ -644,7 +644,7 @@ class Array(Expression):
|
||||
|
||||
def write(self, offset, buf):
|
||||
if not isinstance(buf, (Array, bytearray)):
|
||||
raise TypeError('Array or bytearray expected got {:s}'.format(type(buf)))
|
||||
raise TypeError("Array or bytearray expected got {:s}".format(type(buf)))
|
||||
arr = self
|
||||
for i, val in enumerate(buf):
|
||||
arr = arr.store(offset + i, val)
|
||||
@@ -665,7 +665,7 @@ class Array(Expression):
|
||||
return self.select(self.cast_index(index))
|
||||
|
||||
def __eq__(self, other):
|
||||
#FIXME taint
|
||||
# FIXME taint
|
||||
def compare_buffers(a, b):
|
||||
if len(a) != len(b):
|
||||
return BoolConstant(False)
|
||||
@@ -675,6 +675,7 @@ class Array(Expression):
|
||||
if cond is BoolConstant(False):
|
||||
return BoolConstant(False)
|
||||
return cond
|
||||
|
||||
return compare_buffers(self, other)
|
||||
|
||||
def __ne__(self, other):
|
||||
@@ -708,7 +709,10 @@ class Array(Expression):
|
||||
value = BitVec(size * self.value_bits).cast(value)
|
||||
array = self
|
||||
for offset in range(size):
|
||||
array = array.store(address + offset, BitVecExtract(value, (size - 1 - offset) * self.value_bits, self.value_bits))
|
||||
array = array.store(
|
||||
address + offset,
|
||||
BitVecExtract(value, (size - 1 - offset) * self.value_bits, self.value_bits),
|
||||
)
|
||||
return array
|
||||
|
||||
def write_LE(self, address, value, size):
|
||||
@@ -716,7 +720,10 @@ class Array(Expression):
|
||||
value = BitVec(size * self.value_bits).cast(value)
|
||||
array = self
|
||||
for offset in reversed(range(size)):
|
||||
array = array.store(address + offset, BitVecExtract(value, (size - 1 - offset) * self.value_bits, self.value_bits))
|
||||
array = array.store(
|
||||
address + offset,
|
||||
BitVecExtract(value, (size - 1 - offset) * self.value_bits, self.value_bits),
|
||||
)
|
||||
return array
|
||||
|
||||
def __add__(self, other):
|
||||
@@ -724,11 +731,19 @@ class Array(Expression):
|
||||
raise TypeError("can't concat Array to {}".format(type(other)))
|
||||
if isinstance(other, Array):
|
||||
if self.index_bits != other.index_bits or self.value_bits != other.value_bits:
|
||||
raise ValueError('Array sizes do not match for concatenation')
|
||||
raise ValueError("Array sizes do not match for concatenation")
|
||||
|
||||
from .visitors import simplify
|
||||
#FIXME This should be related to a constrainSet
|
||||
new_arr = ArrayProxy(ArrayVariable(self.index_bits, self.index_max + len(other), self.value_bits, 'concatenation{}'.format(uuid.uuid1())))
|
||||
|
||||
# FIXME This should be related to a constrainSet
|
||||
new_arr = ArrayProxy(
|
||||
ArrayVariable(
|
||||
self.index_bits,
|
||||
self.index_max + len(other),
|
||||
self.value_bits,
|
||||
"concatenation{}".format(uuid.uuid1()),
|
||||
)
|
||||
)
|
||||
for index in range(self.index_max):
|
||||
new_arr[index] = simplify(self[index])
|
||||
for index in range(len(other)):
|
||||
@@ -740,11 +755,19 @@ class Array(Expression):
|
||||
raise TypeError("can't concat Array to {}".format(type(other)))
|
||||
if isinstance(other, Array):
|
||||
if self.index_bits != other.index_bits or self.value_bits != other.value_bits:
|
||||
raise ValueError('Array sizes do not match for concatenation')
|
||||
raise ValueError("Array sizes do not match for concatenation")
|
||||
|
||||
from .visitors import simplify
|
||||
#FIXME This should be related to a constrainSet
|
||||
new_arr = ArrayProxy(ArrayVariable(self.index_bits, self.index_max + len(other), self.value_bits, 'concatenation{}'.format(uuid.uuid1())))
|
||||
|
||||
# FIXME This should be related to a constrainSet
|
||||
new_arr = ArrayProxy(
|
||||
ArrayVariable(
|
||||
self.index_bits,
|
||||
self.index_max + len(other),
|
||||
self.value_bits,
|
||||
"concatenation{}".format(uuid.uuid1()),
|
||||
)
|
||||
)
|
||||
for index in range(len(other)):
|
||||
new_arr[index] = simplify(other[index])
|
||||
_concrete_cache = new_arr._concrete_cache
|
||||
@@ -760,13 +783,15 @@ class ArrayVariable(Array, Variable):
|
||||
|
||||
@property
|
||||
def declaration(self):
|
||||
return f'(declare-fun {self.name} () (Array (_ BitVec {self.index_bits}) (_ BitVec {self.value_bits})))'
|
||||
return f"(declare-fun {self.name} () (Array (_ BitVec {self.index_bits}) (_ BitVec {self.value_bits})))"
|
||||
|
||||
|
||||
class ArrayOperation(Array, Operation):
|
||||
def __init__(self, array, *operands, **kwargs):
|
||||
assert isinstance(array, Array)
|
||||
super().__init__(array.index_bits, array.index_max, array.value_bits, array, *operands, **kwargs)
|
||||
super().__init__(
|
||||
array.index_bits, array.index_max, array.value_bits, array, *operands, **kwargs
|
||||
)
|
||||
|
||||
|
||||
class ArrayStore(ArrayOperation):
|
||||
@@ -833,7 +858,11 @@ class ArraySlice(Array):
|
||||
return self._array.select(index + self._slice_offset)
|
||||
|
||||
def store(self, index, value):
|
||||
return ArraySlice(self._array.store(index + self._slice_offset, value), self._slice_offset, self._slice_size)
|
||||
return ArraySlice(
|
||||
self._array.store(index + self._slice_offset, value),
|
||||
self._slice_offset,
|
||||
self._slice_size,
|
||||
)
|
||||
|
||||
|
||||
class ArrayProxy(Array):
|
||||
@@ -843,7 +872,7 @@ class ArrayProxy(Array):
|
||||
self._concrete_cache = {}
|
||||
self._written = None
|
||||
if isinstance(array, ArrayProxy):
|
||||
#copy constructor
|
||||
# copy constructor
|
||||
super().__init__(array.index_bits, array.index_max, array.value_bits)
|
||||
self._array = array._array
|
||||
self._name = array._name
|
||||
@@ -852,12 +881,12 @@ class ArrayProxy(Array):
|
||||
self._concrete_cache = dict(array._concrete_cache)
|
||||
self._written = set(array.written)
|
||||
elif isinstance(array, ArrayVariable):
|
||||
#fresh array proxy
|
||||
# fresh array proxy
|
||||
super().__init__(array.index_bits, array.index_max, array.value_bits)
|
||||
self._array = array
|
||||
self._name = array.name
|
||||
else:
|
||||
#arrayproxy for a prepopulated array
|
||||
# arrayproxy for a prepopulated array
|
||||
super().__init__(array.index_bits, array.index_max, array.value_bits)
|
||||
self._name = array.underlying_variable.name
|
||||
self._array = array
|
||||
@@ -898,7 +927,10 @@ class ArrayProxy(Array):
|
||||
index = self.cast_index(index)
|
||||
if self.index_max is not None:
|
||||
from .visitors import simplify
|
||||
index = simplify(BitVecITE(self.index_bits, index < 0, self.index_max + index + 1, index))
|
||||
|
||||
index = simplify(
|
||||
BitVecITE(self.index_bits, index < 0, self.index_max + index + 1, index)
|
||||
)
|
||||
if isinstance(index, Constant) and index.value in self._concrete_cache:
|
||||
return self._concrete_cache[index.value]
|
||||
return self._array.select(index)
|
||||
@@ -909,6 +941,7 @@ class ArrayProxy(Array):
|
||||
if not isinstance(value, Expression):
|
||||
value = self.cast_value(value)
|
||||
from .visitors import simplify
|
||||
|
||||
index = simplify(index)
|
||||
if isinstance(index, Constant):
|
||||
self._concrete_cache[index.value] = value
|
||||
@@ -949,19 +982,19 @@ class ArrayProxy(Array):
|
||||
|
||||
def __getstate__(self):
|
||||
state = {}
|
||||
state['_default'] = self._default
|
||||
state['_array'] = self._array
|
||||
state['name'] = self.name
|
||||
state['_concrete_cache'] = self._concrete_cache
|
||||
state['_written'] = self._written
|
||||
state["_default"] = self._default
|
||||
state["_array"] = self._array
|
||||
state["name"] = self.name
|
||||
state["_concrete_cache"] = self._concrete_cache
|
||||
state["_written"] = self._written
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
self._default = state['_default']
|
||||
self._array = state['_array']
|
||||
self._name = state['name']
|
||||
self._concrete_cache = state['_concrete_cache']
|
||||
self._written = state['_written']
|
||||
self._default = state["_default"]
|
||||
self._array = state["_array"]
|
||||
self._name = state["name"]
|
||||
self._concrete_cache = state["_concrete_cache"]
|
||||
self._written = state["_written"]
|
||||
|
||||
def __copy__(self):
|
||||
return ArrayProxy(self)
|
||||
@@ -971,11 +1004,11 @@ class ArrayProxy(Array):
|
||||
# Calculate only first time
|
||||
if self._written is None:
|
||||
written = set()
|
||||
#take out Proxy sleve
|
||||
# take out Proxy sleve
|
||||
array = self._array
|
||||
offset = 0
|
||||
if isinstance(array, ArraySlice):
|
||||
#if it is a proxy over a slice take out the slice too
|
||||
# if it is a proxy over a slice take out the slice too
|
||||
offset = array._slice_offset
|
||||
array = array._array
|
||||
while not isinstance(array, ArrayVariable):
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
|
||||
from .expression import (
|
||||
BitVec, BitVecExtract, BitVecSignExtend, BitVecZeroExtend, BitVecConstant, BitVecConcat, Bool, BitVecITE, BoolConstant, BoolITE
|
||||
BitVec,
|
||||
BitVecExtract,
|
||||
BitVecSignExtend,
|
||||
BitVecZeroExtend,
|
||||
BitVecConstant,
|
||||
BitVecConcat,
|
||||
Bool,
|
||||
BitVecITE,
|
||||
BoolConstant,
|
||||
BoolITE,
|
||||
)
|
||||
from ...utils.helpers import issymbolic
|
||||
import math
|
||||
@@ -13,7 +21,7 @@ def ORD(s):
|
||||
else:
|
||||
return BitVecExtract(s, 0, 7)
|
||||
elif isinstance(s, int):
|
||||
return s & 0xff
|
||||
return s & 0xFF
|
||||
else:
|
||||
return ord(s)
|
||||
|
||||
@@ -25,7 +33,7 @@ def CHR(s):
|
||||
else:
|
||||
return BitVecExtract(s, 0, 8)
|
||||
elif isinstance(s, int):
|
||||
return bytes([s & 0xff])
|
||||
return bytes([s & 0xFF])
|
||||
else:
|
||||
assert len(s) == 1
|
||||
return s
|
||||
@@ -154,10 +162,12 @@ def CONCAT(total_size, *args):
|
||||
arg_size = total_size // len(args)
|
||||
if any(issymbolic(x) for x in args):
|
||||
if len(args) > 1:
|
||||
|
||||
def cast(x):
|
||||
if isinstance(x, int):
|
||||
return BitVecConstant(arg_size, x)
|
||||
return x
|
||||
|
||||
return BitVecConcat(total_size, *list(map(cast, args)))
|
||||
else:
|
||||
return args[0]
|
||||
@@ -191,7 +201,7 @@ def ITEBV(size, cond, true_value, false_value):
|
||||
if isinstance(cond, BitVec):
|
||||
cond = cond.Bool()
|
||||
if isinstance(cond, int):
|
||||
cond = (cond != 0)
|
||||
cond = cond != 0
|
||||
|
||||
assert isinstance(cond, (Bool, bool))
|
||||
assert isinstance(true_value, (BitVec, int))
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
###############################################################################
|
||||
# Solver
|
||||
# A solver maintains a companion smtlib capable process connected via stdio.
|
||||
@@ -30,18 +29,24 @@ from ...utils import config
|
||||
from ...utils.helpers import issymbolic
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
consts = config.get_group('smt')
|
||||
consts.add('timeout', default=240, description='Timeout, in seconds, for each Z3 invocation')
|
||||
consts.add('memory', default=16384, description='Max memory for Z3 to use (in Megabytes)')
|
||||
consts.add('maxsolutions', default=10000, description='Maximum solutions to provide when solving for all values')
|
||||
consts.add('z3_bin', default='z3', description='Z3 binary to use')
|
||||
consts.add('defaultunsat', default=True, description='Consider solver timeouts as unsat core')
|
||||
consts = config.get_group("smt")
|
||||
consts.add("timeout", default=240, description="Timeout, in seconds, for each Z3 invocation")
|
||||
consts.add("memory", default=16384, description="Max memory for Z3 to use (in Megabytes)")
|
||||
consts.add(
|
||||
"maxsolutions",
|
||||
default=10000,
|
||||
description="Maximum solutions to provide when solving for all values",
|
||||
)
|
||||
consts.add("z3_bin", default="z3", description="Z3 binary to use")
|
||||
consts.add("defaultunsat", default=True, description="Consider solver timeouts as unsat core")
|
||||
|
||||
|
||||
# Regular expressions used by the solver
|
||||
RE_GET_EXPR_VALUE_FMT = re.compile('\(\((?P<expr>(.*))\ #x(?P<value>([0-9a-fA-F]*))\)\)')
|
||||
RE_OBJECTIVES_EXPR_VALUE = re.compile('\(objectives.*\((?P<expr>.*) (?P<value>\d*)\).*\).*', re.MULTILINE | re.DOTALL)
|
||||
RE_MIN_MAX_OBJECTIVE_EXPR_VALUE = re.compile('(?P<expr>.*?)\s+\|->\s+(?P<value>.*)', re.DOTALL)
|
||||
RE_GET_EXPR_VALUE_FMT = re.compile("\(\((?P<expr>(.*))\ #x(?P<value>([0-9a-fA-F]*))\)\)")
|
||||
RE_OBJECTIVES_EXPR_VALUE = re.compile(
|
||||
"\(objectives.*\((?P<expr>.*) (?P<value>\d*)\).*\).*", re.MULTILINE | re.DOTALL
|
||||
)
|
||||
RE_MIN_MAX_OBJECTIVE_EXPR_VALUE = re.compile("(?P<expr>.*?)\s+\|->\s+(?P<value>.*)", re.DOTALL)
|
||||
|
||||
|
||||
class SingletonMixin(object):
|
||||
@@ -99,7 +104,7 @@ class Solver(SingletonMixin):
|
||||
:param M: maximum number of iterations allowed
|
||||
"""
|
||||
assert isinstance(X, BitVec)
|
||||
return self.optimize(constraints, X, 'maximize', M)
|
||||
return self.optimize(constraints, X, "maximize", M)
|
||||
|
||||
def min(self, constraints, X: BitVec, M=10000):
|
||||
"""
|
||||
@@ -110,7 +115,7 @@ class Solver(SingletonMixin):
|
||||
:param M: maximum number of iterations allowed
|
||||
"""
|
||||
assert isinstance(X, BitVec)
|
||||
return self.optimize(constraints, X, 'minimize', M)
|
||||
return self.optimize(constraints, X, "minimize", M)
|
||||
|
||||
def minmax(self, constraints, x, iters=10000):
|
||||
"""Returns the min and max possible values for x within given constraints"""
|
||||
@@ -122,7 +127,7 @@ class Solver(SingletonMixin):
|
||||
return x, x
|
||||
|
||||
|
||||
Version = collections.namedtuple('Version', 'major minor patch')
|
||||
Version = collections.namedtuple("Version", "major minor patch")
|
||||
|
||||
|
||||
class Z3Solver(Solver):
|
||||
@@ -135,16 +140,18 @@ class Z3Solver(Solver):
|
||||
super().__init__()
|
||||
self._proc: Popen = None
|
||||
|
||||
self._command = f'{consts.z3_bin} -t:{consts.timeout*1000} -memory:{consts.memory} -smt2 -in'
|
||||
self._command = (
|
||||
f"{consts.z3_bin} -t:{consts.timeout*1000} -memory:{consts.memory} -smt2 -in"
|
||||
)
|
||||
|
||||
# Commands used to initialize z3
|
||||
self._init = [
|
||||
# http://smtlib.cs.uiowa.edu/logics-all.shtml#QF_AUFBV
|
||||
# Closed quantifier-free formulas over the theory of bitvectors and bitvector arrays extended with
|
||||
# free sort and function symbols.
|
||||
'(set-logic QF_AUFBV)',
|
||||
"(set-logic QF_AUFBV)",
|
||||
# The declarations and definitions will be scoped
|
||||
'(set-option :global-decls false)',
|
||||
"(set-option :global-decls false)",
|
||||
]
|
||||
|
||||
self._get_value_fmt = (RE_GET_EXPR_VALUE_FMT, 16)
|
||||
@@ -157,7 +164,7 @@ class Z3Solver(Solver):
|
||||
self.support_maximize = False
|
||||
self.support_minimize = False
|
||||
self.support_reset = True
|
||||
logger.debug('Z3 version: %s', self.version)
|
||||
logger.debug("Z3 version: %s", self.version)
|
||||
|
||||
if self.version >= Version(4, 5, 0):
|
||||
self.support_maximize = False
|
||||
@@ -168,7 +175,7 @@ class Z3Solver(Solver):
|
||||
self.support_minimize = True
|
||||
self.support_reset = False
|
||||
else:
|
||||
logger.debug(' Please install Z3 4.4.1 or newer to get optimization support')
|
||||
logger.debug(" Please install Z3 4.4.1 or newer to get optimization support")
|
||||
|
||||
def _solver_version(self) -> Version:
|
||||
"""
|
||||
@@ -180,16 +187,23 @@ class Z3Solver(Solver):
|
||||
"""
|
||||
self._reset()
|
||||
if self._received_version is None:
|
||||
self._send('(get-info :version)')
|
||||
self._send("(get-info :version)")
|
||||
self._received_version = self._recv()
|
||||
key, version = shlex.split(self._received_version[1:-1])
|
||||
return Version(*map(int, version.split('.')))
|
||||
return Version(*map(int, version.split(".")))
|
||||
|
||||
def _start_proc(self):
|
||||
"""Spawns z3 solver process"""
|
||||
assert '_proc' not in dir(self) or self._proc is None
|
||||
assert "_proc" not in dir(self) or self._proc is None
|
||||
try:
|
||||
self._proc = Popen(shlex.split(self._command), stdin=PIPE, stdout=PIPE, bufsize=0, universal_newlines=True, close_fds=True)
|
||||
self._proc = Popen(
|
||||
shlex.split(self._command),
|
||||
stdin=PIPE,
|
||||
stdout=PIPE,
|
||||
bufsize=0,
|
||||
universal_newlines=True,
|
||||
close_fds=True,
|
||||
)
|
||||
except OSError as e:
|
||||
print(e, "Probably too many cached expressions? visitors._cache...")
|
||||
# Z3 was removed from the system in the middle of operation
|
||||
@@ -268,11 +282,11 @@ class Z3Solver(Solver):
|
||||
|
||||
:param cmd: a SMTLIBv2 command (ex. (check-sat))
|
||||
"""
|
||||
#logger.debug('>%s', cmd)
|
||||
#print (">",self._proc.stdin.name, threading.get_ident())
|
||||
# logger.debug('>%s', cmd)
|
||||
# print (">",self._proc.stdin.name, threading.get_ident())
|
||||
try:
|
||||
self._proc.stdout.flush()
|
||||
self._proc.stdin.write(f'{cmd}\n')
|
||||
self._proc.stdin.write(f"{cmd}\n")
|
||||
except IOError as e:
|
||||
raise SolverError(str(e))
|
||||
|
||||
@@ -287,16 +301,16 @@ class Z3Solver(Solver):
|
||||
left += l
|
||||
right += r
|
||||
|
||||
buf = ''.join(bufl).strip()
|
||||
buf = "".join(bufl).strip()
|
||||
|
||||
#logger.debug('<%s', buf)
|
||||
if '(error' in bufl[0]:
|
||||
# logger.debug('<%s', buf)
|
||||
if "(error" in bufl[0]:
|
||||
raise Exception(f"Error in smtlib: {bufl[0]}")
|
||||
return buf
|
||||
|
||||
def __readline_and_count(self):
|
||||
buf = self._proc.stdout.readline()
|
||||
return buf, buf.count('('), buf.count(')')
|
||||
return buf, buf.count("("), buf.count(")")
|
||||
|
||||
# UTILS: check-sat get-value
|
||||
def _is_sat(self) -> bool:
|
||||
@@ -307,26 +321,26 @@ class Z3Solver(Solver):
|
||||
"""
|
||||
logger.debug("Solver.check() ")
|
||||
start = time.time()
|
||||
self._send('(check-sat)')
|
||||
self._send("(check-sat)")
|
||||
status = self._recv()
|
||||
logger.debug("Check took %s seconds (%s)", time.time() - start, status)
|
||||
if status not in ('sat', 'unsat', 'unknown'):
|
||||
if status not in ("sat", "unsat", "unknown"):
|
||||
raise SolverError(status)
|
||||
if consts.defaultunsat:
|
||||
if status == 'unknown':
|
||||
logger.info('Found an unknown core, probably a solver timeout')
|
||||
status = 'unsat'
|
||||
if status == "unknown":
|
||||
logger.info("Found an unknown core, probably a solver timeout")
|
||||
status = "unsat"
|
||||
|
||||
if status == 'unknown':
|
||||
if status == "unknown":
|
||||
raise SolverUnknown(status)
|
||||
|
||||
return status == 'sat'
|
||||
return status == "sat"
|
||||
|
||||
def _assert(self, expression: Bool):
|
||||
"""Auxiliary method to send an assert"""
|
||||
assert isinstance(expression, Bool)
|
||||
smtlib = translate_to_smtlib(expression)
|
||||
self._send('(assert %s)' % smtlib)
|
||||
self._send("(assert %s)" % smtlib)
|
||||
|
||||
def _getvalue(self, expression):
|
||||
"""
|
||||
@@ -343,21 +357,21 @@ class Z3Solver(Solver):
|
||||
result = bytearray()
|
||||
for c in expression:
|
||||
expression_str = translate_to_smtlib(c)
|
||||
self._send('(get-value (%s))' % expression_str)
|
||||
self._send("(get-value (%s))" % expression_str)
|
||||
response = self._recv()
|
||||
result.append(int('0x{:s}'.format(response.split(expression_str)[1][3:-2]), 16))
|
||||
result.append(int("0x{:s}".format(response.split(expression_str)[1][3:-2]), 16))
|
||||
return bytes(result)
|
||||
else:
|
||||
self._send('(get-value (%s))' % expression.name)
|
||||
self._send("(get-value (%s))" % expression.name)
|
||||
ret = self._recv()
|
||||
assert ret.startswith('((') and ret.endswith('))'), ret
|
||||
assert ret.startswith("((") and ret.endswith("))"), ret
|
||||
|
||||
if isinstance(expression, Bool):
|
||||
return {'true': True, 'false': False}[ret[2:-2].split(' ')[1]]
|
||||
return {"true": True, "false": False}[ret[2:-2].split(" ")[1]]
|
||||
elif isinstance(expression, BitVec):
|
||||
pattern, base = self._get_value_fmt
|
||||
m = pattern.match(ret)
|
||||
expr, value = m.group('expr'), m.group('value')
|
||||
expr, value = m.group("expr"), m.group("value")
|
||||
return int(value, base)
|
||||
|
||||
raise NotImplementedError("_getvalue only implemented for Bool and BitVec")
|
||||
@@ -365,11 +379,11 @@ class Z3Solver(Solver):
|
||||
# push pop
|
||||
def _push(self):
|
||||
"""Pushes and save the current constraint store and state."""
|
||||
self._send('(push 1)')
|
||||
self._send("(push 1)")
|
||||
|
||||
def _pop(self):
|
||||
"""Recall the last pushed constraint store and state."""
|
||||
self._send('(pop 1)')
|
||||
self._send("(pop 1)")
|
||||
|
||||
def can_be_true(self, constraints, expression):
|
||||
"""Check if two potentially symbolic values can be equal"""
|
||||
@@ -404,9 +418,15 @@ class Z3Solver(Solver):
|
||||
elif isinstance(expression, BitVec):
|
||||
var = temp_cs.new_bitvec(expression.size)
|
||||
elif isinstance(expression, Array):
|
||||
var = temp_cs.new_array(index_max=expression.index_max, value_bits=expression.value_bits, taint=expression.taint).array
|
||||
var = temp_cs.new_array(
|
||||
index_max=expression.index_max,
|
||||
value_bits=expression.value_bits,
|
||||
taint=expression.taint,
|
||||
).array
|
||||
else:
|
||||
raise NotImplementedError(f"get_all_values only implemented for {type(expression)} expression type.")
|
||||
raise NotImplementedError(
|
||||
f"get_all_values only implemented for {type(expression)} expression type."
|
||||
)
|
||||
|
||||
temp_cs.add(var == expression)
|
||||
self._reset(temp_cs.to_string(related_to=var))
|
||||
@@ -440,40 +460,40 @@ class Z3Solver(Solver):
|
||||
:param goal: goal to achieve, either 'maximize' or 'minimize'
|
||||
:param M: maximum number of iterations allowed
|
||||
"""
|
||||
assert goal in ('maximize', 'minimize')
|
||||
assert goal in ("maximize", "minimize")
|
||||
assert isinstance(x, BitVec)
|
||||
operation = {'maximize': Operators.UGE, 'minimize': Operators.ULE}[goal]
|
||||
operation = {"maximize": Operators.UGE, "minimize": Operators.ULE}[goal]
|
||||
|
||||
with constraints as temp_cs:
|
||||
X = temp_cs.new_bitvec(x.size)
|
||||
temp_cs.add(X == x)
|
||||
aux = temp_cs.new_bitvec(X.size, name='optimized_')
|
||||
aux = temp_cs.new_bitvec(X.size, name="optimized_")
|
||||
self._reset(temp_cs.to_string(related_to=X))
|
||||
self._send(aux.declaration)
|
||||
|
||||
if getattr(self, f'support_{goal}'):
|
||||
if getattr(self, f"support_{goal}"):
|
||||
self._push()
|
||||
try:
|
||||
self._assert(operation(X, aux))
|
||||
self._send('(%s %s)' % (goal, aux.name))
|
||||
self._send('(check-sat)')
|
||||
self._send("(%s %s)" % (goal, aux.name))
|
||||
self._send("(check-sat)")
|
||||
_status = self._recv()
|
||||
if _status not in ('sat', 'unsat', 'unknown'):
|
||||
if _status not in ("sat", "unsat", "unknown"):
|
||||
# Minimize (or Maximize) sometimes prints the objective before the status
|
||||
# This will be a line like NAME |-> VALUE
|
||||
maybe_sat = self._recv()
|
||||
if maybe_sat == 'sat':
|
||||
if maybe_sat == "sat":
|
||||
m = RE_MIN_MAX_OBJECTIVE_EXPR_VALUE.match(_status)
|
||||
expr, value = m.group('expr'), m.group('value')
|
||||
expr, value = m.group("expr"), m.group("value")
|
||||
assert expr == aux.name
|
||||
return int(value)
|
||||
elif _status == 'sat':
|
||||
elif _status == "sat":
|
||||
ret = self._recv()
|
||||
if not (ret.startswith('(') and ret.endswith(')')):
|
||||
raise SolverError('bad output on max, z3 may have been killed')
|
||||
if not (ret.startswith("(") and ret.endswith(")")):
|
||||
raise SolverError("bad output on max, z3 may have been killed")
|
||||
|
||||
m = RE_OBJECTIVES_EXPR_VALUE.match(ret)
|
||||
expr, value = m.group('expr'), m.group('value')
|
||||
expr, value = m.group("expr"), m.group("value")
|
||||
assert expr == aux.name
|
||||
return int(value)
|
||||
finally:
|
||||
@@ -481,7 +501,7 @@ class Z3Solver(Solver):
|
||||
self._reset(temp_cs)
|
||||
self._send(aux.declaration)
|
||||
|
||||
operation = {'maximize': Operators.UGT, 'minimize': Operators.ULT}[goal]
|
||||
operation = {"maximize": Operators.UGT, "minimize": Operators.ULT}[goal]
|
||||
self._assert(aux == X)
|
||||
last_value = None
|
||||
i = 0
|
||||
@@ -517,15 +537,15 @@ class Z3Solver(Solver):
|
||||
|
||||
self._reset(temp_cs)
|
||||
if not self._is_sat():
|
||||
raise SolverError('Model is not available')
|
||||
raise SolverError("Model is not available")
|
||||
|
||||
for i in range(expression.index_max):
|
||||
self._send('(get-value (%s))' % var[i].name)
|
||||
self._send("(get-value (%s))" % var[i].name)
|
||||
ret = self._recv()
|
||||
assert ret.startswith('((') and ret.endswith('))')
|
||||
assert ret.startswith("((") and ret.endswith("))")
|
||||
pattern, base = self._get_value_fmt
|
||||
m = pattern.match(ret)
|
||||
expr, value = m.group('expr'), m.group('value')
|
||||
expr, value = m.group("expr"), m.group("value")
|
||||
result.append(int(value, base))
|
||||
return bytes(result)
|
||||
|
||||
@@ -534,18 +554,18 @@ class Z3Solver(Solver):
|
||||
self._reset(temp_cs)
|
||||
|
||||
if not self._is_sat():
|
||||
raise SolverError('Model is not available')
|
||||
raise SolverError("Model is not available")
|
||||
|
||||
self._send('(get-value (%s))' % var.name)
|
||||
self._send("(get-value (%s))" % var.name)
|
||||
ret = self._recv()
|
||||
if not (ret.startswith('((') and ret.endswith('))')):
|
||||
raise SolverError('SMTLIB error parsing response: %s' % ret)
|
||||
if not (ret.startswith("((") and ret.endswith("))")):
|
||||
raise SolverError("SMTLIB error parsing response: %s" % ret)
|
||||
|
||||
if isinstance(expression, Bool):
|
||||
return {'true': True, 'false': False}[ret[2:-2].split(' ')[1]]
|
||||
return {"true": True, "false": False}[ret[2:-2].split(" ")[1]]
|
||||
if isinstance(expression, BitVec):
|
||||
pattern, base = self._get_value_fmt
|
||||
m = pattern.match(ret)
|
||||
expr, value = m.group('expr'), m.group('value')
|
||||
expr, value = m.group("expr"), m.group("value")
|
||||
return int(value, base)
|
||||
raise NotImplementedError("get_value only implemented for Bool and BitVec")
|
||||
|
||||
@@ -3,6 +3,7 @@ from .expression import *
|
||||
from functools import lru_cache
|
||||
import logging
|
||||
import operator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -54,7 +55,7 @@ class Visitor:
|
||||
assert expression.__class__.__mro__[-1] is object
|
||||
for cls in expression.__class__.__mro__:
|
||||
sort = cls.__name__
|
||||
methodname = 'visit_%s' % sort
|
||||
methodname = "visit_%s" % sort
|
||||
if hasattr(self, methodname):
|
||||
value = getattr(self, methodname)(expression, *args)
|
||||
if value is not None:
|
||||
@@ -111,6 +112,7 @@ class Visitor:
|
||||
if isinstance(expression, Operation):
|
||||
if any(x is not y for x, y in zip(expression.operands, operands)):
|
||||
import copy
|
||||
|
||||
aux = copy.copy(expression)
|
||||
aux._operands = operands
|
||||
return aux
|
||||
@@ -122,13 +124,13 @@ class Translator(Visitor):
|
||||
"""
|
||||
|
||||
def _method(self, expression, *args):
|
||||
#Special case. Need to get the unsleeved version of the array
|
||||
# Special case. Need to get the unsleeved version of the array
|
||||
if isinstance(expression, ArrayProxy):
|
||||
expression = expression.array
|
||||
assert expression.__class__.__mro__[-1] is object
|
||||
for cls in expression.__class__.__mro__:
|
||||
sort = cls.__name__
|
||||
methodname = f'visit_{sort:s}'
|
||||
methodname = f"visit_{sort:s}"
|
||||
if hasattr(self, methodname):
|
||||
value = getattr(self, methodname)(expression, *args)
|
||||
if value is not None:
|
||||
@@ -177,13 +179,13 @@ def get_depth(exp):
|
||||
class PrettyPrinter(Visitor):
|
||||
def __init__(self, depth=None, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.output = ''
|
||||
self.output = ""
|
||||
self.indent = 0
|
||||
self.depth = depth
|
||||
|
||||
def _print(self, s, e=None):
|
||||
self.output += ' ' * self.indent + str(s) # + '(%016x)'%hash(e)
|
||||
self.output += '\n'
|
||||
self.output += " " * self.indent + str(s) # + '(%016x)'%hash(e)
|
||||
self.output += "\n"
|
||||
|
||||
def visit(self, expression):
|
||||
"""
|
||||
@@ -202,7 +204,7 @@ class PrettyPrinter(Visitor):
|
||||
assert expression.__class__.__mro__[-1] is object
|
||||
for cls in expression.__class__.__mro__:
|
||||
sort = cls.__name__
|
||||
methodname = 'visit_%s' % sort
|
||||
methodname = "visit_%s" % sort
|
||||
method = getattr(self, methodname, None)
|
||||
if method is not None:
|
||||
method(expression, *args)
|
||||
@@ -216,28 +218,31 @@ class PrettyPrinter(Visitor):
|
||||
for o in expression.operands:
|
||||
self.visit(o)
|
||||
else:
|
||||
self._print('...')
|
||||
self._print("...")
|
||||
self.indent -= 2
|
||||
return ''
|
||||
return ""
|
||||
|
||||
def visit_BitVecExtract(self, expression):
|
||||
self._print(expression.__class__.__name__ + '{%d:%d}' % (expression.begining, expression.end), expression)
|
||||
self._print(
|
||||
expression.__class__.__name__ + "{%d:%d}" % (expression.begining, expression.end),
|
||||
expression,
|
||||
)
|
||||
self.indent += 2
|
||||
if self.depth is None or self.indent < self.depth * 2:
|
||||
for o in expression.operands:
|
||||
self.visit(o)
|
||||
else:
|
||||
self._print('...')
|
||||
self._print("...")
|
||||
self.indent -= 2
|
||||
return ''
|
||||
return ""
|
||||
|
||||
def visit_Constant(self, expression):
|
||||
self._print(expression.value)
|
||||
return ''
|
||||
return ""
|
||||
|
||||
def visit_Variable(self, expression):
|
||||
self._print(expression.name)
|
||||
return ''
|
||||
return ""
|
||||
|
||||
@property
|
||||
def result(self):
|
||||
@@ -256,25 +261,27 @@ class ConstantFolderSimplifier(Visitor):
|
||||
def __init__(self, **kw):
|
||||
super().__init__(**kw)
|
||||
|
||||
operations = {BitVecAdd: operator.__add__,
|
||||
BitVecSub: operator.__sub__,
|
||||
BitVecMul: operator.__mul__,
|
||||
BitVecDiv: operator.__truediv__,
|
||||
BitVecShiftLeft: operator.__lshift__,
|
||||
BitVecShiftRight: operator.__rshift__,
|
||||
BitVecAnd: operator.__and__,
|
||||
BitVecOr: operator.__or__,
|
||||
BitVecXor: operator.__xor__,
|
||||
BitVecNot: operator.__not__,
|
||||
BitVecNeg: operator.__invert__,
|
||||
LessThan: operator.__lt__,
|
||||
LessOrEqual: operator.__le__,
|
||||
Equal: operator.__eq__,
|
||||
GreaterThan: operator.__gt__,
|
||||
GreaterOrEqual: operator.__ge__,
|
||||
BoolAnd: operator.__and__,
|
||||
BoolOr: operator.__or__,
|
||||
BoolNot: operator.__not__}
|
||||
operations = {
|
||||
BitVecAdd: operator.__add__,
|
||||
BitVecSub: operator.__sub__,
|
||||
BitVecMul: operator.__mul__,
|
||||
BitVecDiv: operator.__truediv__,
|
||||
BitVecShiftLeft: operator.__lshift__,
|
||||
BitVecShiftRight: operator.__rshift__,
|
||||
BitVecAnd: operator.__and__,
|
||||
BitVecOr: operator.__or__,
|
||||
BitVecXor: operator.__xor__,
|
||||
BitVecNot: operator.__not__,
|
||||
BitVecNeg: operator.__invert__,
|
||||
LessThan: operator.__lt__,
|
||||
LessOrEqual: operator.__le__,
|
||||
Equal: operator.__eq__,
|
||||
GreaterThan: operator.__gt__,
|
||||
GreaterOrEqual: operator.__ge__,
|
||||
BoolAnd: operator.__and__,
|
||||
BoolOr: operator.__or__,
|
||||
BoolNot: operator.__not__,
|
||||
}
|
||||
|
||||
def visit_BitVecConcat(self, expression, *operands):
|
||||
if all(isinstance(o, Constant) for o in operands):
|
||||
@@ -298,7 +305,7 @@ class ConstantFolderSimplifier(Visitor):
|
||||
begining = expression.begining
|
||||
end = expression.end
|
||||
value = value >> begining
|
||||
mask = 2**(end - begining + 1) - 1
|
||||
mask = 2 ** (end - begining + 1) - 1
|
||||
value = value & mask
|
||||
return BitVecConstant(expression.size, value, taint=expression.taint)
|
||||
|
||||
@@ -317,8 +324,7 @@ class ConstantFolderSimplifier(Visitor):
|
||||
def visit_Operation(self, expression, *operands):
|
||||
""" constant folding, if all operands of an expression are a Constant do the math """
|
||||
operation = self.operations.get(type(expression), None)
|
||||
if operation is not None and \
|
||||
all(isinstance(o, Constant) for o in operands):
|
||||
if operation is not None and all(isinstance(o, Constant) for o in operands):
|
||||
value = operation(*(x.value for x in operands))
|
||||
if isinstance(expression, BitVec):
|
||||
return BitVecConstant(expression.size, value, taint=expression.taint)
|
||||
@@ -348,9 +354,7 @@ class ArithmeticSimplifier(Visitor):
|
||||
|
||||
@staticmethod
|
||||
def _same_constant(a, b):
|
||||
return isinstance(a, Constant) and\
|
||||
isinstance(b, Constant) and\
|
||||
a.value == b.value or a is b
|
||||
return isinstance(a, Constant) and isinstance(b, Constant) and a.value == b.value or a is b
|
||||
|
||||
@staticmethod
|
||||
def _changed(expression, operands):
|
||||
@@ -445,11 +449,19 @@ class ArithmeticSimplifier(Visitor):
|
||||
new_operands.append(item)
|
||||
bitcount += item.size
|
||||
if begining != expression.begining:
|
||||
return BitVecExtract(BitVecConcat(sum([x.size for x in new_operands]), *reversed(new_operands)),
|
||||
begining, expression.size, taint=expression.taint)
|
||||
return BitVecExtract(
|
||||
BitVecConcat(sum([x.size for x in new_operands]), *reversed(new_operands)),
|
||||
begining,
|
||||
expression.size,
|
||||
taint=expression.taint,
|
||||
)
|
||||
if isinstance(op, (BitVecAnd, BitVecOr, BitVecXor)):
|
||||
bitoperand_a, bitoperand_b = op.operands
|
||||
return op.__class__(BitVecExtract(bitoperand_a, begining, expression.size), BitVecExtract(bitoperand_b, begining, expression.size), taint=expression.taint)
|
||||
return op.__class__(
|
||||
BitVecExtract(bitoperand_a, begining, expression.size),
|
||||
BitVecExtract(bitoperand_b, begining, expression.size),
|
||||
taint=expression.taint,
|
||||
)
|
||||
|
||||
def visit_BitVecAdd(self, expression, *operands):
|
||||
""" a + 0 ==> a
|
||||
@@ -480,7 +492,14 @@ class ArithmeticSimplifier(Visitor):
|
||||
subleft = left.operands[0]
|
||||
subright = left.operands[1]
|
||||
if isinstance(subright, Constant):
|
||||
return BitVecSub(subleft, BitVecConstant(subleft.size, subright.value + right.value, taint=subright.taint | right.taint))
|
||||
return BitVecSub(
|
||||
subleft,
|
||||
BitVecConstant(
|
||||
subleft.size,
|
||||
subright.value + right.value,
|
||||
taint=subright.taint | right.taint,
|
||||
),
|
||||
)
|
||||
|
||||
def visit_BitVecOr(self, expression, *operands):
|
||||
""" a | 0 => a
|
||||
@@ -556,10 +575,19 @@ class ArithmeticSimplifier(Visitor):
|
||||
|
||||
# props are slow and using them in tight loops should be avoided, esp when they offer no additional validation
|
||||
# arr._operands[1] = arr.index, arr._operands[0] = arr.array
|
||||
while isinstance(arr, ArrayStore) and isinstance(arr._operands[1], BitVecConstant) and arr._operands[1]._value != ival:
|
||||
while (
|
||||
isinstance(arr, ArrayStore)
|
||||
and isinstance(arr._operands[1], BitVecConstant)
|
||||
and arr._operands[1]._value != ival
|
||||
):
|
||||
arr = arr._operands[0] # arr.array
|
||||
|
||||
if isinstance(index, BitVecConstant) and isinstance(arr, ArrayStore) and isinstance(arr.index, BitVecConstant) and arr.index.value == index.value:
|
||||
if (
|
||||
isinstance(index, BitVecConstant)
|
||||
and isinstance(arr, ArrayStore)
|
||||
and isinstance(arr.index, BitVecConstant)
|
||||
and arr.index.value == index.value
|
||||
):
|
||||
return arr.value
|
||||
else:
|
||||
if arr is not expression.array:
|
||||
@@ -616,10 +644,11 @@ def simplify(expression):
|
||||
class TranslatorSmtlib(Translator):
|
||||
""" Simple visitor to translate an expression to its smtlib representation
|
||||
"""
|
||||
|
||||
unique = 0
|
||||
|
||||
def __init__(self, use_bindings=False, *args, **kw):
|
||||
assert 'bindings' not in kw
|
||||
assert "bindings" not in kw
|
||||
super().__init__(*args, **kw)
|
||||
self.use_bindings = use_bindings
|
||||
self._bindings_cache = {}
|
||||
@@ -633,7 +662,7 @@ class TranslatorSmtlib(Translator):
|
||||
return self._bindings_cache[smtlib]
|
||||
|
||||
TranslatorSmtlib.unique += 1
|
||||
name = 'a_%d' % TranslatorSmtlib.unique
|
||||
name = "a_%d" % TranslatorSmtlib.unique
|
||||
|
||||
self._bindings.append((name, expression, smtlib))
|
||||
|
||||
@@ -645,56 +674,56 @@ class TranslatorSmtlib(Translator):
|
||||
return self._bindings
|
||||
|
||||
translation_table = {
|
||||
BoolNot: 'not',
|
||||
BoolEq: '=',
|
||||
BoolAnd: 'and',
|
||||
BoolOr: 'or',
|
||||
BoolXor: 'xor',
|
||||
BoolITE: 'ite',
|
||||
BitVecAdd: 'bvadd',
|
||||
BitVecSub: 'bvsub',
|
||||
BitVecMul: 'bvmul',
|
||||
BitVecDiv: 'bvsdiv',
|
||||
BitVecUnsignedDiv: 'bvudiv',
|
||||
BitVecMod: 'bvsmod',
|
||||
BitVecRem: 'bvsrem',
|
||||
BitVecUnsignedRem: 'bvurem',
|
||||
BitVecShiftLeft: 'bvshl',
|
||||
BitVecShiftRight: 'bvlshr',
|
||||
BitVecArithmeticShiftLeft: 'bvashl',
|
||||
BitVecArithmeticShiftRight: 'bvashr',
|
||||
BitVecAnd: 'bvand',
|
||||
BitVecOr: 'bvor',
|
||||
BitVecXor: 'bvxor',
|
||||
BitVecNot: 'bvnot',
|
||||
BitVecNeg: 'bvneg',
|
||||
LessThan: 'bvslt',
|
||||
LessOrEqual: 'bvsle',
|
||||
Equal: '=',
|
||||
GreaterThan: 'bvsgt',
|
||||
GreaterOrEqual: 'bvsge',
|
||||
UnsignedLessThan: 'bvult',
|
||||
UnsignedLessOrEqual: 'bvule',
|
||||
UnsignedGreaterThan: 'bvugt',
|
||||
UnsignedGreaterOrEqual: 'bvuge',
|
||||
BitVecSignExtend: '(_ sign_extend %d)',
|
||||
BitVecZeroExtend: '(_ zero_extend %d)',
|
||||
BitVecExtract: '(_ extract %d %d)',
|
||||
BitVecConcat: 'concat',
|
||||
BitVecITE: 'ite',
|
||||
ArrayStore: 'store',
|
||||
ArraySelect: 'select',
|
||||
BoolNot: "not",
|
||||
BoolEq: "=",
|
||||
BoolAnd: "and",
|
||||
BoolOr: "or",
|
||||
BoolXor: "xor",
|
||||
BoolITE: "ite",
|
||||
BitVecAdd: "bvadd",
|
||||
BitVecSub: "bvsub",
|
||||
BitVecMul: "bvmul",
|
||||
BitVecDiv: "bvsdiv",
|
||||
BitVecUnsignedDiv: "bvudiv",
|
||||
BitVecMod: "bvsmod",
|
||||
BitVecRem: "bvsrem",
|
||||
BitVecUnsignedRem: "bvurem",
|
||||
BitVecShiftLeft: "bvshl",
|
||||
BitVecShiftRight: "bvlshr",
|
||||
BitVecArithmeticShiftLeft: "bvashl",
|
||||
BitVecArithmeticShiftRight: "bvashr",
|
||||
BitVecAnd: "bvand",
|
||||
BitVecOr: "bvor",
|
||||
BitVecXor: "bvxor",
|
||||
BitVecNot: "bvnot",
|
||||
BitVecNeg: "bvneg",
|
||||
LessThan: "bvslt",
|
||||
LessOrEqual: "bvsle",
|
||||
Equal: "=",
|
||||
GreaterThan: "bvsgt",
|
||||
GreaterOrEqual: "bvsge",
|
||||
UnsignedLessThan: "bvult",
|
||||
UnsignedLessOrEqual: "bvule",
|
||||
UnsignedGreaterThan: "bvugt",
|
||||
UnsignedGreaterOrEqual: "bvuge",
|
||||
BitVecSignExtend: "(_ sign_extend %d)",
|
||||
BitVecZeroExtend: "(_ zero_extend %d)",
|
||||
BitVecExtract: "(_ extract %d %d)",
|
||||
BitVecConcat: "concat",
|
||||
BitVecITE: "ite",
|
||||
ArrayStore: "store",
|
||||
ArraySelect: "select",
|
||||
}
|
||||
|
||||
def visit_BitVecConstant(self, expression):
|
||||
assert isinstance(expression, BitVecConstant)
|
||||
if expression.size == 1:
|
||||
return '#' + bin(expression.value & expression.mask)[1:]
|
||||
return "#" + bin(expression.value & expression.mask)[1:]
|
||||
else:
|
||||
return '#x%0*x' % (int(expression.size / 4), expression.value & expression.mask)
|
||||
return "#x%0*x" % (int(expression.size / 4), expression.value & expression.mask)
|
||||
|
||||
def visit_BoolConstant(self, expression):
|
||||
return expression.value and 'true' or 'false'
|
||||
return expression.value and "true" or "false"
|
||||
|
||||
def visit_Variable(self, expression):
|
||||
return expression.name
|
||||
@@ -704,7 +733,7 @@ class TranslatorSmtlib(Translator):
|
||||
if isinstance(expression.array, ArrayStore):
|
||||
array_smt = self._add_binding(expression.array, array_smt)
|
||||
|
||||
return '(select %s %s)' % (array_smt, index_smt)
|
||||
return "(select %s %s)" % (array_smt, index_smt)
|
||||
|
||||
def visit_Operation(self, expression, *operands):
|
||||
operation = self.translation_table[type(expression)]
|
||||
@@ -714,7 +743,7 @@ class TranslatorSmtlib(Translator):
|
||||
operation = operation % (expression.end, expression.begining)
|
||||
|
||||
operands = [self._add_binding(*x) for x in zip(expression.operands, operands)]
|
||||
return '(%s %s)' % (operation, ' '.join(operands))
|
||||
return "(%s %s)" % (operation, " ".join(operands))
|
||||
|
||||
@property
|
||||
def results(self):
|
||||
@@ -725,7 +754,7 @@ class TranslatorSmtlib(Translator):
|
||||
output = super().result
|
||||
if self.use_bindings:
|
||||
for name, expr, smtlib in reversed(self._bindings):
|
||||
output = '( let ((%s %s)) %s )' % (name, smtlib, output)
|
||||
output = "( let ((%s %s)) %s )" % (name, smtlib, output)
|
||||
return output
|
||||
|
||||
|
||||
|
||||
+59
-40
@@ -25,7 +25,7 @@ class AbandonState(TerminateState):
|
||||
execution is finished
|
||||
"""
|
||||
|
||||
def __init__(self, message='Abandoned state'):
|
||||
def __init__(self, message="Abandoned state"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
@@ -38,17 +38,18 @@ class Concretize(StateException):
|
||||
#Fixme Doc.
|
||||
|
||||
"""
|
||||
_ValidPolicies = ['MINMAX', 'ALL', 'SAMPLED', 'ONE']
|
||||
|
||||
_ValidPolicies = ["MINMAX", "ALL", "SAMPLED", "ONE"]
|
||||
|
||||
def __init__(self, message, expression, setstate=None, policy=None, **kwargs):
|
||||
if policy is None:
|
||||
policy = 'ALL'
|
||||
policy = "ALL"
|
||||
if policy not in self._ValidPolicies:
|
||||
raise Exception(f'Policy ({policy}) must be one of: {", ".join(self._ValidPolicies)}')
|
||||
self.expression = expression
|
||||
self.setstate = setstate
|
||||
self.policy = policy
|
||||
self.message = f'Concretize: {message} (Policy: {policy})'
|
||||
self.message = f"Concretize: {message} (Policy: {policy})"
|
||||
super().__init__(**kwargs)
|
||||
|
||||
|
||||
@@ -62,8 +63,8 @@ class ForkState(Concretize):
|
||||
"""
|
||||
|
||||
def __init__(self, message, expression, **kwargs):
|
||||
assert isinstance(expression, Bool), 'Need a Bool to fork a state in two states'
|
||||
super().__init__(message, expression, policy='ALL', **kwargs)
|
||||
assert isinstance(expression, Bool), "Need a Bool to fork a state in two states"
|
||||
super().__init__(message, expression, policy="ALL", **kwargs)
|
||||
|
||||
|
||||
class StateBase(Eventful):
|
||||
@@ -89,30 +90,30 @@ class StateBase(Eventful):
|
||||
|
||||
def __getstate__(self):
|
||||
state = super().__getstate__()
|
||||
state['platform'] = self._platform
|
||||
state['constraints'] = self._constraints
|
||||
state['input_symbols'] = self._input_symbols
|
||||
state['child'] = self._child
|
||||
state['context'] = self._context
|
||||
state["platform"] = self._platform
|
||||
state["constraints"] = self._constraints
|
||||
state["input_symbols"] = self._input_symbols
|
||||
state["child"] = self._child
|
||||
state["context"] = self._context
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
super().__setstate__(state)
|
||||
self._platform = state['platform']
|
||||
self._constraints = state['constraints']
|
||||
self._input_symbols = state['input_symbols']
|
||||
self._child = state['child']
|
||||
self._context = state['context']
|
||||
self._platform = state["platform"]
|
||||
self._constraints = state["constraints"]
|
||||
self._input_symbols = state["input_symbols"]
|
||||
self._child = state["child"]
|
||||
self._context = state["context"]
|
||||
# 33
|
||||
# Events are lost in serialization and fork !!
|
||||
self.forward_events_from(self._platform)
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return getattr(self, '_id', None)
|
||||
return getattr(self, "_id", None)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<State object with id {self.id}>'
|
||||
return f"<State object with id {self.id}>"
|
||||
|
||||
# Fixme(felipe) change for with "state.cow_copy() as st_temp":.
|
||||
# This need to change. this is the center of ALL the problems. re. CoW
|
||||
@@ -189,16 +190,22 @@ class StateBase(Eventful):
|
||||
|
||||
:return: :class:`~manticore.core.smtlib.expression.Expression` representing the buffer.
|
||||
"""
|
||||
label = options.get('label')
|
||||
label = options.get("label")
|
||||
avoid_collisions = False
|
||||
if label is None:
|
||||
label = 'buffer'
|
||||
label = "buffer"
|
||||
avoid_collisions = True
|
||||
taint = options.get('taint', frozenset())
|
||||
expr = self._constraints.new_array(name=label, index_max=nbytes, value_bits=8, taint=taint, avoid_collisions=avoid_collisions)
|
||||
taint = options.get("taint", frozenset())
|
||||
expr = self._constraints.new_array(
|
||||
name=label,
|
||||
index_max=nbytes,
|
||||
value_bits=8,
|
||||
taint=taint,
|
||||
avoid_collisions=avoid_collisions,
|
||||
)
|
||||
self._input_symbols.append(expr)
|
||||
|
||||
if options.get('cstring', False):
|
||||
if options.get("cstring", False):
|
||||
for i in range(nbytes - 1):
|
||||
self._constraints.add(expr[i] != 0)
|
||||
|
||||
@@ -218,10 +225,12 @@ class StateBase(Eventful):
|
||||
assert nbits in (1, 4, 8, 16, 32, 64, 128, 256)
|
||||
avoid_collisions = False
|
||||
if label is None:
|
||||
label = 'val'
|
||||
label = "val"
|
||||
avoid_collisions = True
|
||||
|
||||
expr = self._constraints.new_bitvec(nbits, name=label, taint=taint, avoid_collisions=avoid_collisions)
|
||||
expr = self._constraints.new_bitvec(
|
||||
nbits, name=label, taint=taint, avoid_collisions=avoid_collisions
|
||||
)
|
||||
self._input_symbols.append(expr)
|
||||
return expr
|
||||
|
||||
@@ -233,13 +242,13 @@ class StateBase(Eventful):
|
||||
symbolic = self.migrate_expression(symbolic)
|
||||
|
||||
vals = []
|
||||
if policy == 'MINMAX':
|
||||
if policy == "MINMAX":
|
||||
vals = self._solver.minmax(self._constraints, symbolic)
|
||||
elif policy == 'MAX':
|
||||
elif policy == "MAX":
|
||||
vals = self._solver.max(self._constraints, symbolic)
|
||||
elif policy == 'MIN':
|
||||
elif policy == "MIN":
|
||||
vals = self._solver.min(self._constraints, symbolic)
|
||||
elif policy == 'SAMPLED':
|
||||
elif policy == "SAMPLED":
|
||||
m, M = self._solver.minmax(self._constraints, symbolic)
|
||||
vals += [m, M]
|
||||
if M - m > 3:
|
||||
@@ -252,29 +261,33 @@ class StateBase(Eventful):
|
||||
if maxcount <= len(vals):
|
||||
break
|
||||
if M - m > 1000 and maxcount > len(vals):
|
||||
vals += self._solver.get_all_values(self._constraints, symbolic,
|
||||
maxcnt=maxcount - len(vals), silent=True)
|
||||
elif policy == 'ONE':
|
||||
vals += self._solver.get_all_values(
|
||||
self._constraints, symbolic, maxcnt=maxcount - len(vals), silent=True
|
||||
)
|
||||
elif policy == "ONE":
|
||||
vals = [self._solver.get_value(self._constraints, symbolic)]
|
||||
else:
|
||||
assert policy == 'ALL'
|
||||
vals = self._solver.get_all_values(self._constraints, symbolic, maxcnt=maxcount, silent=True)
|
||||
assert policy == "ALL"
|
||||
vals = self._solver.get_all_values(
|
||||
self._constraints, symbolic, maxcnt=maxcount, silent=True
|
||||
)
|
||||
|
||||
return tuple(set(vals))
|
||||
|
||||
@property
|
||||
def _solver(self):
|
||||
from .smtlib import Z3Solver
|
||||
|
||||
return Z3Solver.instance() # solver
|
||||
|
||||
def migrate_expression(self, expression):
|
||||
if not issymbolic(expression):
|
||||
return expression
|
||||
migration_map = self.context.get('migration_map')
|
||||
migration_map = self.context.get("migration_map")
|
||||
if migration_map is None:
|
||||
migration_map = {}
|
||||
migrated_expression = self.constraints.migrate(expression, name_migration_map=migration_map)
|
||||
self.context['migration_map'] = migration_map
|
||||
self.context["migration_map"] = migration_map
|
||||
return migrated_expression
|
||||
|
||||
def is_feasible(self):
|
||||
@@ -290,7 +303,9 @@ class StateBase(Eventful):
|
||||
|
||||
def must_be_true(self, expr):
|
||||
expr = self.migrate_expression(expr)
|
||||
return self._solver.can_be_true(self._constraints, expr) and not self._solver.can_be_true(self._constraints, expr == False)
|
||||
return self._solver.can_be_true(self._constraints, expr) and not self._solver.can_be_true(
|
||||
self._constraints, expr == False
|
||||
)
|
||||
|
||||
def solve_one(self, expr, constrain=False):
|
||||
"""
|
||||
@@ -306,7 +321,7 @@ class StateBase(Eventful):
|
||||
value = self._solver.get_value(self._constraints, expr)
|
||||
if constrain:
|
||||
self.constrain(expr == value)
|
||||
#Include forgiveness here
|
||||
# Include forgiveness here
|
||||
if isinstance(value, bytearray):
|
||||
value = bytes(value)
|
||||
return value
|
||||
@@ -388,7 +403,9 @@ class StateBase(Eventful):
|
||||
cs_to_use.add(c == result[-1])
|
||||
return result
|
||||
|
||||
def symbolicate_buffer(self, data, label='INPUT', wildcard='+', string=False, taint=frozenset()):
|
||||
def symbolicate_buffer(
|
||||
self, data, label="INPUT", wildcard="+", string=False, taint=frozenset()
|
||||
):
|
||||
"""Mark parts of a buffer as symbolic (demarked by the wildcard byte)
|
||||
|
||||
:param str data: The string to symbolicate. If no wildcard bytes are provided,
|
||||
@@ -405,7 +422,9 @@ class StateBase(Eventful):
|
||||
"""
|
||||
if wildcard in data:
|
||||
size = len(data)
|
||||
symb = self._constraints.new_array(name=label, index_max=size, taint=taint, avoid_collisions=True)
|
||||
symb = self._constraints.new_array(
|
||||
name=label, index_max=size, taint=taint, avoid_collisions=True
|
||||
)
|
||||
self._input_symbols.append(symb)
|
||||
|
||||
tmp = []
|
||||
|
||||
@@ -7,7 +7,7 @@ import os
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
#logger.setLevel(9)
|
||||
# logger.setLevel(9)
|
||||
|
||||
|
||||
# Workers
|
||||
@@ -17,6 +17,7 @@ logger = logging.getLogger(__name__)
|
||||
# WorkerProcess: runs on a different process - Full multiprocessing
|
||||
# WorkerMultiprocessing: --planned-- runs on a different computer
|
||||
|
||||
|
||||
class Worker:
|
||||
"""
|
||||
A Manticore Worker.
|
||||
@@ -56,7 +57,12 @@ class Worker:
|
||||
|
||||
def run(self, *args):
|
||||
# This controls the main symbolic execution loop of one of the workers
|
||||
logger.debug("Starting Manticore Symbolic Emulator Worker %d. Pid %d Tid %d).", self.id, os.getpid(), threading.get_ident())
|
||||
logger.debug(
|
||||
"Starting Manticore Symbolic Emulator Worker %d. Pid %d Tid %d).",
|
||||
self.id,
|
||||
os.getpid(),
|
||||
threading.get_ident(),
|
||||
)
|
||||
|
||||
m = self.manticore
|
||||
m._is_main = False # This will mark our copy of manticore
|
||||
@@ -102,7 +108,10 @@ class Worker:
|
||||
# Allows to terminate manticore worker on user request
|
||||
# even in the middle of an execution
|
||||
logger.debug("[%r] Running", self.id)
|
||||
assert current_state.id in m._busy_states and current_state.id not in m._ready_states
|
||||
assert (
|
||||
current_state.id in m._busy_states
|
||||
and current_state.id not in m._ready_states
|
||||
)
|
||||
|
||||
# This does not hold the lock so we may loss some event
|
||||
# flickering
|
||||
@@ -134,7 +143,7 @@ class Worker:
|
||||
except TerminateState as exc:
|
||||
logger.debug("[%r] Debug State %r %r", self.id, current_state, exc)
|
||||
# Notify this state is done
|
||||
m._publish('will_terminate_state', current_state, exc)
|
||||
m._publish("will_terminate_state", current_state, exc)
|
||||
# Update the stored version of the current state
|
||||
|
||||
m._save(current_state, state_id=current_state.id)
|
||||
@@ -143,11 +152,12 @@ class Worker:
|
||||
# this run
|
||||
m._terminate_state(current_state.id)
|
||||
|
||||
m._publish('did_terminate_state', current_state, exc)
|
||||
m._publish("did_terminate_state", current_state, exc)
|
||||
current_state = None
|
||||
|
||||
except (Exception, AssertionError) as exc:
|
||||
import traceback
|
||||
|
||||
formatted = traceback.format_exc()
|
||||
logger.error("Exception in state %r: %r\n%s ", self.id, exc, formatted)
|
||||
# Internal Exception
|
||||
@@ -158,10 +168,10 @@ class Worker:
|
||||
# Update the stored version of the current state
|
||||
# Saved to a fresh id in case other worker have an old
|
||||
# version this state cached over the old id
|
||||
m._publish('will_kill_state', current_state, exc)
|
||||
m._publish("will_kill_state", current_state, exc)
|
||||
m._save(current_state, state_id=current_state.id)
|
||||
m._kill_state(current_state.id)
|
||||
m._publish('did_kill_state', current_state, exc)
|
||||
m._publish("did_kill_state", current_state, exc)
|
||||
current_state = None
|
||||
break
|
||||
|
||||
@@ -175,6 +185,7 @@ class WorkerSingle(Worker):
|
||||
""" A single worker that will run in the current process and current thread.
|
||||
As this will not provide any concurrency is normally only used for
|
||||
profiling underlying arch emulation and debugging."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, single=True, **kwargs)
|
||||
|
||||
@@ -187,6 +198,7 @@ class WorkerSingle(Worker):
|
||||
|
||||
class WorkerThread(Worker):
|
||||
""" A worker thread """
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._t = None
|
||||
@@ -202,6 +214,7 @@ class WorkerThread(Worker):
|
||||
|
||||
class WorkerProcess(Worker):
|
||||
""" A worker process """
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._p = None
|
||||
|
||||
+63
-53
@@ -11,8 +11,8 @@ from contextlib import contextmanager
|
||||
try:
|
||||
from contextlib import nullcontext
|
||||
except ImportError:
|
||||
class nullcontext():
|
||||
|
||||
class nullcontext:
|
||||
def __init__(self, enter_result=None):
|
||||
self.enter_result = enter_result
|
||||
|
||||
@@ -22,6 +22,7 @@ except ImportError:
|
||||
def __exit__(self, *excinfo):
|
||||
pass
|
||||
|
||||
|
||||
import time
|
||||
import os
|
||||
import errno
|
||||
@@ -33,9 +34,11 @@ from .state import StateBase
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
consts = config.get_group('workspace')
|
||||
consts.add('prefix', default='mcore_', description="The prefix to use for output and workspace directories")
|
||||
consts.add('dir', default='.', description="Location of where to create workspace directories")
|
||||
consts = config.get_group("workspace")
|
||||
consts.add(
|
||||
"prefix", default="mcore_", description="The prefix to use for output and workspace directories"
|
||||
)
|
||||
consts.add("dir", default=".", description="Location of where to create workspace directories")
|
||||
|
||||
|
||||
class Testcase:
|
||||
@@ -52,8 +55,8 @@ class Testcase:
|
||||
def num(self):
|
||||
return self._num
|
||||
|
||||
def open_stream(self, suffix='', binary=False):
|
||||
stream_name = f'{self._prefix}_{self._num:08x}.{suffix}'
|
||||
def open_stream(self, suffix="", binary=False):
|
||||
stream_name = f"{self._prefix}_{self._num:08x}.{suffix}"
|
||||
return self._ws.save_stream(stream_name, binary=binary)
|
||||
|
||||
|
||||
@@ -82,22 +85,26 @@ class Store:
|
||||
:param str desc: Store descriptor
|
||||
:return: Store instance
|
||||
"""
|
||||
type_, uri = ('fs', None) if desc is None else desc.split(':', 1)
|
||||
type_, uri = ("fs", None) if desc is None else desc.split(":", 1)
|
||||
for subclass in cls.__subclasses__():
|
||||
if subclass.store_type == type_:
|
||||
return subclass(uri)
|
||||
raise NotImplementedError(f"Storage type '{type_}' not supported.")
|
||||
|
||||
def __init__(self, uri, state_serialization_method='pickle'):
|
||||
assert self.__class__ != Store, "The Store class can not be instantiated (create a subclass)"
|
||||
def __init__(self, uri, state_serialization_method="pickle"):
|
||||
assert (
|
||||
self.__class__ != Store
|
||||
), "The Store class can not be instantiated (create a subclass)"
|
||||
|
||||
self.uri = uri
|
||||
self._sub = []
|
||||
|
||||
if state_serialization_method == 'pickle':
|
||||
if state_serialization_method == "pickle":
|
||||
self._serializer = PickleSerializer()
|
||||
else:
|
||||
raise NotImplementedError(f"Pickling method '{state_serialization_method}' not supported.")
|
||||
raise NotImplementedError(
|
||||
f"Pickling method '{state_serialization_method}' not supported."
|
||||
)
|
||||
|
||||
# save_value/load_value and save_stream/load_stream are implemented in terms of each other. A backing store
|
||||
# can choose the pair it's best optimized for.
|
||||
@@ -197,7 +204,8 @@ class FilesystemStore(Store):
|
||||
"""
|
||||
A directory-backed Manticore workspace
|
||||
"""
|
||||
store_type = 'fs'
|
||||
|
||||
store_type = "fs"
|
||||
|
||||
def __init__(self, uri=None):
|
||||
"""
|
||||
@@ -208,7 +216,7 @@ class FilesystemStore(Store):
|
||||
uri = os.path.abspath(tempfile.mkdtemp(prefix=consts.prefix, dir=consts.dir))
|
||||
|
||||
if os.path.exists(uri):
|
||||
assert os.path.isdir(uri), 'Store must be a directory'
|
||||
assert os.path.isdir(uri), "Store must be a directory"
|
||||
else:
|
||||
os.mkdir(uri)
|
||||
|
||||
@@ -216,7 +224,7 @@ class FilesystemStore(Store):
|
||||
|
||||
@contextmanager
|
||||
def lock(self):
|
||||
lockfile = os.path.join(self.uri, '.lock')
|
||||
lockfile = os.path.join(self.uri, ".lock")
|
||||
with self._tlock:
|
||||
while True:
|
||||
try:
|
||||
@@ -232,7 +240,7 @@ class FilesystemStore(Store):
|
||||
break
|
||||
|
||||
@contextmanager
|
||||
def stream(self, key, mode='r', lock=False):
|
||||
def stream(self, key, mode="r", lock=False):
|
||||
"""
|
||||
Yield a file object representing `key`
|
||||
|
||||
@@ -259,7 +267,7 @@ class FilesystemStore(Store):
|
||||
:param lock: exclusive access if True
|
||||
:return:
|
||||
"""
|
||||
mode = 'wb' if binary else 'w'
|
||||
mode = "wb" if binary else "w"
|
||||
with self.stream(key, mode, lock) as f:
|
||||
yield f
|
||||
|
||||
@@ -271,7 +279,7 @@ class FilesystemStore(Store):
|
||||
:param lock: exclusive access if True
|
||||
:return:
|
||||
"""
|
||||
mode = 'rb' if binary else 'r'
|
||||
mode = "rb" if binary else "r"
|
||||
with self.stream(key, mode, lock) as f:
|
||||
yield f
|
||||
|
||||
@@ -302,7 +310,8 @@ class MemoryStore(Store):
|
||||
NOTE: This is mostly used for experimentation and testing functionality.
|
||||
Can not be used with multiple workers!
|
||||
"""
|
||||
store_type = 'mem'
|
||||
|
||||
store_type = "mem"
|
||||
|
||||
# TODO(yan): Once we get a global config store, check it to make sure
|
||||
# we're executing in a single-worker or test environment.
|
||||
@@ -330,13 +339,13 @@ class MemoryStore(Store):
|
||||
yield
|
||||
|
||||
@contextmanager
|
||||
def stream(self, key, mode='r', lock=False):
|
||||
def stream(self, key, mode="r", lock=False):
|
||||
if lock:
|
||||
raise Exception("mem: does not support concurrency")
|
||||
if 'b' in mode:
|
||||
s = io.BytesIO(self._data.get(key, b''))
|
||||
if "b" in mode:
|
||||
s = io.BytesIO(self._data.get(key, b""))
|
||||
else:
|
||||
s = io.StringIO(self._data.get(key, ''))
|
||||
s = io.StringIO(self._data.get(key, ""))
|
||||
yield s
|
||||
self._data[key] = s.getvalue()
|
||||
|
||||
@@ -345,7 +354,8 @@ class RedisStore(Store):
|
||||
"""
|
||||
A redis-backed Manticore workspace
|
||||
"""
|
||||
store_type = 'redis'
|
||||
|
||||
store_type = "redis"
|
||||
|
||||
def __init__(self, uri=None):
|
||||
"""
|
||||
@@ -355,7 +365,7 @@ class RedisStore(Store):
|
||||
# Local import to avoid an explicit dependency
|
||||
import redis
|
||||
|
||||
hostname, port = uri.split(':')
|
||||
hostname, port = uri.split(":")
|
||||
self._client = redis.StrictRedis(host=hostname, port=int(port), db=0)
|
||||
|
||||
super().__init__(uri)
|
||||
@@ -402,18 +412,18 @@ class Workspace:
|
||||
else:
|
||||
self._store = Store.fromdescriptor(store_or_desc)
|
||||
self._serializer = PickleSerializer()
|
||||
self._prefix = 'state_'
|
||||
self._suffix = '.pkl'
|
||||
self._prefix = "state_"
|
||||
self._suffix = ".pkl"
|
||||
|
||||
@property
|
||||
def uri(self):
|
||||
return self._store.uri
|
||||
|
||||
def try_loading_workspace(self):
|
||||
state_names = self._store.ls(f'{self._prefix}*')
|
||||
state_names = self._store.ls(f"{self._prefix}*")
|
||||
|
||||
def get_state_id(name):
|
||||
return int(name[len(self._prefix):-len(self._suffix)], 16)
|
||||
return int(name[len(self._prefix) : -len(self._suffix)], 16)
|
||||
|
||||
state_ids = list(map(get_state_id, state_names))
|
||||
|
||||
@@ -430,14 +440,14 @@ class Workspace:
|
||||
"""
|
||||
with self._store.lock():
|
||||
try:
|
||||
with self._store.load_stream('.state_id') as f:
|
||||
with self._store.load_stream(".state_id") as f:
|
||||
last_id = int(f.read())
|
||||
except Exception as e:
|
||||
last_id = 0
|
||||
else:
|
||||
last_id += 1
|
||||
with self._store.save_stream('.state_id') as f:
|
||||
f.write(f'{last_id}')
|
||||
with self._store.save_stream(".state_id") as f:
|
||||
f.write(f"{last_id}")
|
||||
f.flush()
|
||||
return last_id
|
||||
|
||||
@@ -449,7 +459,7 @@ class Workspace:
|
||||
:return: The deserialized state
|
||||
:rtype: State
|
||||
"""
|
||||
return self._store.load_state(f'{self._prefix}{state_id:08x}{self._suffix}', delete=delete)
|
||||
return self._store.load_state(f"{self._prefix}{state_id:08x}{self._suffix}", delete=delete)
|
||||
|
||||
def save_state(self, state, state_id=None):
|
||||
"""
|
||||
@@ -466,7 +476,7 @@ class Workspace:
|
||||
else:
|
||||
self.rm_state(state_id)
|
||||
|
||||
self._store.save_state(state, f'{self._prefix}{state_id:08x}{self._suffix}')
|
||||
self._store.save_state(state, f"{self._prefix}{state_id:08x}{self._suffix}")
|
||||
return state_id
|
||||
|
||||
def rm_state(self, state_id):
|
||||
@@ -475,7 +485,7 @@ class Workspace:
|
||||
|
||||
:param state_id: The state reference of what to load
|
||||
"""
|
||||
return self._store.rm(f'{self._prefix}{state_id:08x}{self._suffix}')
|
||||
return self._store.rm(f"{self._prefix}{state_id:08x}{self._suffix}")
|
||||
|
||||
|
||||
class ManticoreOutput:
|
||||
@@ -493,11 +503,11 @@ class ManticoreOutput:
|
||||
|
||||
:param desc: A descriptor ('type:uri') of where to write output.
|
||||
"""
|
||||
self._named_key_prefix = 'test'
|
||||
self._named_key_prefix = "test"
|
||||
self._descriptor = desc
|
||||
self._store = Store.fromdescriptor(desc)
|
||||
|
||||
def testcase(self, prefix='test'):
|
||||
def testcase(self, prefix="test"):
|
||||
return Testcase(self, prefix)
|
||||
|
||||
@property
|
||||
@@ -515,7 +525,7 @@ class ManticoreOutput:
|
||||
:rtype: str
|
||||
"""
|
||||
if self._descriptor is None:
|
||||
self._descriptor = f'{self._store.store_type}:{self._store.uri}'
|
||||
self._descriptor = f"{self._store.store_type}:{self._store.uri}"
|
||||
|
||||
return self._descriptor
|
||||
|
||||
@@ -525,7 +535,7 @@ class ManticoreOutput:
|
||||
|
||||
:rtype: int
|
||||
"""
|
||||
filename = '.testcase_id'
|
||||
filename = ".testcase_id"
|
||||
with self._store.lock():
|
||||
try:
|
||||
with self._store.stream(filename, "r") as f:
|
||||
@@ -535,7 +545,7 @@ class ManticoreOutput:
|
||||
else:
|
||||
last_id += 1
|
||||
with self._store.stream(filename, "w") as f:
|
||||
f.write(f'{last_id}')
|
||||
f.write(f"{last_id}")
|
||||
f.flush()
|
||||
return last_id
|
||||
|
||||
@@ -549,7 +559,7 @@ class ManticoreOutput:
|
||||
return last_id
|
||||
|
||||
def _named_key(self, suffix):
|
||||
return f'{self._named_key_prefix}_{self._last_id:08x}.{suffix}'
|
||||
return f"{self._named_key_prefix}_{self._last_id:08x}.{suffix}"
|
||||
|
||||
def save_stream(self, key, *rest, **kwargs):
|
||||
return self._store.save_stream(key, *rest, **kwargs)
|
||||
@@ -566,8 +576,8 @@ class ManticoreOutput:
|
||||
with self._store.save_stream(self._named_key(name), binary=binary, lock=lock) as s:
|
||||
yield s
|
||||
|
||||
#Remove/move ...
|
||||
def save_testcase(self, state, testcase, message=''):
|
||||
# Remove/move ...
|
||||
def save_testcase(self, state, testcase, message=""):
|
||||
"""
|
||||
Save the environment from `state` to storage. Return a state id
|
||||
describing it, which should be an int or a string.
|
||||
@@ -592,14 +602,14 @@ class ManticoreOutput:
|
||||
data = data.encode()
|
||||
stream.write(data)
|
||||
|
||||
#self._store.save_state(state, self._named_key('pkl'))
|
||||
# self._store.save_state(state, self._named_key('pkl'))
|
||||
return testcase
|
||||
|
||||
@staticmethod
|
||||
def save_summary(testcase, state, message):
|
||||
with testcase.open_stream('messages') as summary:
|
||||
with testcase.open_stream("messages") as summary:
|
||||
summary.write(f"Command line:\n '{' '.join(sys.argv)}'\n")
|
||||
summary.write(f'Status:\n {message}\n\n')
|
||||
summary.write(f"Status:\n {message}\n\n")
|
||||
|
||||
memories = set()
|
||||
for cpu in filter(None, state.platform.procs):
|
||||
@@ -607,7 +617,7 @@ class ManticoreOutput:
|
||||
summary.write(f"================ PROC: {idx:02d} ================\n")
|
||||
summary.write("Memory:\n")
|
||||
if hash(cpu.memory) not in memories:
|
||||
summary.write(str(cpu.memory).replace('\n', '\n '))
|
||||
summary.write(str(cpu.memory).replace("\n", "\n "))
|
||||
memories.add(hash(cpu.memory))
|
||||
|
||||
summary.write(f"CPU:\n{cpu}")
|
||||
@@ -620,23 +630,23 @@ class ManticoreOutput:
|
||||
|
||||
@staticmethod
|
||||
def save_trace(testcase, state):
|
||||
with testcase.open_stream('trace') as f:
|
||||
if 'trace' not in state.context:
|
||||
with testcase.open_stream("trace") as f:
|
||||
if "trace" not in state.context:
|
||||
return
|
||||
for entry in state.context['trace']:
|
||||
f.write(f'0x{entry:x}\n')
|
||||
for entry in state.context["trace"]:
|
||||
f.write(f"0x{entry:x}\n")
|
||||
|
||||
@staticmethod
|
||||
def save_constraints(testcase, state):
|
||||
# XXX(yan): We want to conditionally enable this check
|
||||
# assert solver.check(state.constraints)
|
||||
|
||||
with testcase.open_stream('smt') as f:
|
||||
with testcase.open_stream("smt") as f:
|
||||
f.write(str(state.constraints))
|
||||
|
||||
@staticmethod
|
||||
def save_input_symbols(testcase, state):
|
||||
with testcase.open_stream('input') as f:
|
||||
with testcase.open_stream("input") as f:
|
||||
for symbol in state.input_symbols:
|
||||
buf = Z3Solver().get_value(state.constraints, symbol)
|
||||
f.write(f'{symbol.name}: {buf!r}\n')
|
||||
f.write(f"{symbol.name}: {buf!r}\n")
|
||||
|
||||
@@ -2,9 +2,21 @@
|
||||
from .abi import ABI
|
||||
from .manticore import ManticoreEVM
|
||||
from .state import State
|
||||
from .detectors import Detector, DetectEnvInstruction, DetectExternalCallAndLeak, DetectReentrancySimple, \
|
||||
DetectSuicidal, DetectUnusedRetVal, DetectDelegatecall, DetectIntegerOverflow, DetectInvalid, \
|
||||
DetectReentrancyAdvanced, DetectUninitializedMemory, DetectUninitializedStorage, DetectRaceCondition
|
||||
from .detectors import (
|
||||
Detector,
|
||||
DetectEnvInstruction,
|
||||
DetectExternalCallAndLeak,
|
||||
DetectReentrancySimple,
|
||||
DetectSuicidal,
|
||||
DetectUnusedRetVal,
|
||||
DetectDelegatecall,
|
||||
DetectIntegerOverflow,
|
||||
DetectInvalid,
|
||||
DetectReentrancyAdvanced,
|
||||
DetectUninitializedMemory,
|
||||
DetectUninitializedStorage,
|
||||
DetectRaceCondition,
|
||||
)
|
||||
from .account import EVMAccount, EVMContract
|
||||
from .abi import ABI
|
||||
from .solidity import SolidityMetadata
|
||||
|
||||
+58
-44
@@ -20,21 +20,22 @@ class ABI:
|
||||
and for contract-to-contract interaction.
|
||||
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _type_size(ty):
|
||||
""" Calculate `static` type size """
|
||||
if ty[0] in ('int', 'uint', 'bytesM', 'function'):
|
||||
if ty[0] in ("int", "uint", "bytesM", "function"):
|
||||
return 32
|
||||
elif ty[0] in ('tuple'):
|
||||
elif ty[0] in ("tuple"):
|
||||
result = 0
|
||||
for ty_i in ty[1]:
|
||||
result += ABI._type_size(ty_i)
|
||||
return result
|
||||
elif ty[0] in ('array'):
|
||||
elif ty[0] in ("array"):
|
||||
rep = ty[1]
|
||||
result = 32 # offset link
|
||||
return result
|
||||
elif ty[0] in ('bytes', 'string'):
|
||||
elif ty[0] in ("bytes", "string"):
|
||||
result = 32 # offset link
|
||||
return result
|
||||
raise ValueError
|
||||
@@ -43,14 +44,16 @@ class ABI:
|
||||
def _check_and_warn_num_args(type_spec, *args):
|
||||
num_args = len(args)
|
||||
|
||||
no_declared_args = '()' in type_spec
|
||||
no_declared_args = "()" in type_spec
|
||||
if no_declared_args:
|
||||
num_sig_args = 0
|
||||
else:
|
||||
num_sig_args = len(type_spec.split(','))
|
||||
num_sig_args = len(type_spec.split(","))
|
||||
|
||||
if num_args != num_sig_args:
|
||||
logger.warning(f'Number of provided arguments ({num_args}) does not match number of arguments in signature: {type_spec}')
|
||||
logger.warning(
|
||||
f"Number of provided arguments ({num_args}) does not match number of arguments in signature: {type_spec}"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def function_call(type_spec, *args):
|
||||
@@ -64,7 +67,7 @@ class ABI:
|
||||
ABI._check_and_warn_num_args(type_spec, *args)
|
||||
|
||||
result = ABI.function_selector(type_spec) # Funcid
|
||||
result += ABI.serialize(m.group('type'), *args)
|
||||
result += ABI.serialize(m.group("type"), *args)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
@@ -80,9 +83,9 @@ class ABI:
|
||||
# Catch and rebrand parsing errors
|
||||
raise EthereumError(str(e))
|
||||
|
||||
if parsed_ty[0] != 'tuple':
|
||||
if parsed_ty[0] != "tuple":
|
||||
if len(values) > 1:
|
||||
raise ValueError('too many values passed for non-tuple')
|
||||
raise ValueError("too many values passed for non-tuple")
|
||||
values = values[0]
|
||||
if isinstance(values, str):
|
||||
values = values.encode()
|
||||
@@ -102,28 +105,30 @@ class ABI:
|
||||
result = bytearray()
|
||||
dyn_result = bytearray()
|
||||
|
||||
if ty[0] == 'int':
|
||||
if ty[0] == "int":
|
||||
result += ABI._serialize_int(value, size=ty[1] // 8, padding=32 - ty[1] // 8)
|
||||
elif ty[0] == 'uint':
|
||||
elif ty[0] == "uint":
|
||||
result += ABI._serialize_uint(value, size=ty[1] // 8, padding=32 - ty[1] // 8)
|
||||
elif ty[0] == 'bytesM':
|
||||
elif ty[0] == "bytesM":
|
||||
nbytes = ty[1]
|
||||
if len(value) > nbytes:
|
||||
raise EthereumError('bytesM: value length exceeds size of bytes{} type'.format(nbytes))
|
||||
raise EthereumError(
|
||||
"bytesM: value length exceeds size of bytes{} type".format(nbytes)
|
||||
)
|
||||
result += ABI._serialize_bytes(value)
|
||||
elif ty[0] in ('bytes', 'string'):
|
||||
elif ty[0] in ("bytes", "string"):
|
||||
result += ABI._serialize_uint(dyn_offset)
|
||||
dyn_result += ABI._serialize_uint(len(value))
|
||||
dyn_result += ABI._serialize_bytes(value)
|
||||
elif ty[0] == 'function':
|
||||
elif ty[0] == "function":
|
||||
result = ABI._serialize_uint(value[0], 20)
|
||||
result += value[1] + bytearray('\0' * 8)
|
||||
result += value[1] + bytearray("\0" * 8)
|
||||
assert len(result) == 32
|
||||
elif ty[0] == 'tuple':
|
||||
elif ty[0] == "tuple":
|
||||
sub_result, sub_dyn_result = ABI._serialize_tuple(ty[1], value, dyn_offset)
|
||||
result += sub_result
|
||||
dyn_result += sub_dyn_result
|
||||
elif ty[0] == 'array':
|
||||
elif ty[0] == "array":
|
||||
rep = ty[1]
|
||||
base_type = ty[2]
|
||||
sub_result, sub_dyn_result = ABI._serialize_array(rep, base_type, value, dyn_offset)
|
||||
@@ -141,14 +146,16 @@ class ABI:
|
||||
:param value:
|
||||
:type value: str or bytearray or Array
|
||||
"""
|
||||
return value + bytearray(b'\x00' * (32 - len(value)))
|
||||
return value + bytearray(b"\x00" * (32 - len(value)))
|
||||
|
||||
@staticmethod
|
||||
def _serialize_tuple(types, value, dyn_offset=None):
|
||||
result = bytearray()
|
||||
dyn_result = bytearray()
|
||||
if len(types) != len(value):
|
||||
raise ValueError(f"The number of values to serialize is {'less' if len(value) < len(types) else 'greater'} than the number of types")
|
||||
raise ValueError(
|
||||
f"The number of values to serialize is {'less' if len(value) < len(types) else 'greater'} than the number of types"
|
||||
)
|
||||
for ty_i, value_i in zip(types, value):
|
||||
result_i, dyn_result_i = ABI._serialize(ty_i, value_i, dyn_offset + len(dyn_result))
|
||||
result += result_i
|
||||
@@ -168,7 +175,9 @@ class ABI:
|
||||
sub_result += ABI._serialize_uint(len(value))
|
||||
|
||||
for value_i in value:
|
||||
result_i, dyn_result_i = ABI._serialize(base_type, value_i, dyn_offset + len(dyn_result))
|
||||
result_i, dyn_result_i = ABI._serialize(
|
||||
base_type, value_i, dyn_offset + len(dyn_result)
|
||||
)
|
||||
sub_result += result_i
|
||||
sub_dyn_result += dyn_result_i
|
||||
|
||||
@@ -195,12 +204,12 @@ class ABI:
|
||||
assert isinstance(data, (bytearray, Array))
|
||||
|
||||
m = re.match(r"(?P<name>[a-zA-Z_0-9]+)(?P<type>\(.*\))", type_spec)
|
||||
if m and m.group('name'):
|
||||
if m and m.group("name"):
|
||||
# Type has function name. Let's take the function id from the data
|
||||
# This does not check that the encoded func_id is valid
|
||||
# func_id = ABI.function_selector(type_spec)
|
||||
result = (data[:4],)
|
||||
ty = m.group('type')
|
||||
ty = m.group("type")
|
||||
result += (ABI._deserialize(abitypes.parse(ty), data[4:]),)
|
||||
else:
|
||||
# No function name, just types
|
||||
@@ -214,33 +223,33 @@ class ABI:
|
||||
def _deserialize(ty, buf, offset=0):
|
||||
assert isinstance(buf, (bytearray, Array))
|
||||
result = None
|
||||
if ty[0] == 'int':
|
||||
result = ABI._deserialize_int(buf[offset:offset + 32], nbytes=ty[1] // 8)
|
||||
elif ty[0] == 'uint':
|
||||
result = ABI._deserialize_uint(buf[offset:offset + 32], nbytes=ty[1] // 8)
|
||||
elif ty[0] == 'bytesM':
|
||||
result = buf[offset:offset + ty[1]]
|
||||
elif ty[0] == 'function':
|
||||
address = Operators.ZEXTEND(ABI._readBE(buf[offset:offset + 20], 20), 256)
|
||||
func_id = buf[offset + 20:offset + 24]
|
||||
if ty[0] == "int":
|
||||
result = ABI._deserialize_int(buf[offset : offset + 32], nbytes=ty[1] // 8)
|
||||
elif ty[0] == "uint":
|
||||
result = ABI._deserialize_uint(buf[offset : offset + 32], nbytes=ty[1] // 8)
|
||||
elif ty[0] == "bytesM":
|
||||
result = buf[offset : offset + ty[1]]
|
||||
elif ty[0] == "function":
|
||||
address = Operators.ZEXTEND(ABI._readBE(buf[offset : offset + 20], 20), 256)
|
||||
func_id = buf[offset + 20 : offset + 24]
|
||||
result = (address, func_id)
|
||||
elif ty[0] in ('bytes', 'string'):
|
||||
dyn_offset = ABI._deserialize_int(buf[offset:offset + 32])
|
||||
size = ABI._deserialize_int(buf[dyn_offset:dyn_offset + 32])
|
||||
result = buf[dyn_offset + 32:dyn_offset + 32 + size]
|
||||
elif ty[0] in ('tuple'):
|
||||
elif ty[0] in ("bytes", "string"):
|
||||
dyn_offset = ABI._deserialize_int(buf[offset : offset + 32])
|
||||
size = ABI._deserialize_int(buf[dyn_offset : dyn_offset + 32])
|
||||
result = buf[dyn_offset + 32 : dyn_offset + 32 + size]
|
||||
elif ty[0] in ("tuple"):
|
||||
result = ()
|
||||
current_off = 0
|
||||
for ty_i in ty[1]:
|
||||
result += (ABI._deserialize(ty_i, buf, offset), )
|
||||
result += (ABI._deserialize(ty_i, buf, offset),)
|
||||
offset += ABI._type_size(ty_i)
|
||||
elif ty[0] in ('array'):
|
||||
elif ty[0] in ("array"):
|
||||
result = []
|
||||
dyn_offset = ABI._deserialize_int(buf[offset:offset + 32])
|
||||
dyn_offset = ABI._deserialize_int(buf[offset : offset + 32])
|
||||
rep = ty[1]
|
||||
ty_size = ABI._type_size(ty[2])
|
||||
if rep is None:
|
||||
rep = ABI._deserialize_int(buf[dyn_offset:dyn_offset + 32])
|
||||
rep = ABI._deserialize_int(buf[dyn_offset : dyn_offset + 32])
|
||||
dyn_offset += 32
|
||||
for _ in range(rep):
|
||||
result.append(ABI._deserialize(ty[2], buf, dyn_offset))
|
||||
@@ -259,11 +268,14 @@ class ABI:
|
||||
raise ValueError
|
||||
|
||||
from .account import EVMAccount # because of circular import
|
||||
|
||||
if not isinstance(value, (int, BitVec, EVMAccount)):
|
||||
raise ValueError
|
||||
if issymbolic(value):
|
||||
# FIXME This temporary array variable should be obtained from a specific constraint store
|
||||
bytes = ArrayVariable(index_bits=256, index_max=32, value_bits=8, name='temp{}'.format(uuid.uuid1()))
|
||||
bytes = ArrayVariable(
|
||||
index_bits=256, index_max=32, value_bits=8, name="temp{}".format(uuid.uuid1())
|
||||
)
|
||||
if value.size <= size * 8:
|
||||
value = Operators.ZEXTEND(value, size * 8)
|
||||
else:
|
||||
@@ -290,7 +302,9 @@ class ABI:
|
||||
if not isinstance(value, (int, BitVec)):
|
||||
raise ValueError
|
||||
if issymbolic(value):
|
||||
buf = ArrayVariable(index_bits=256, index_max=32, value_bits=8, name='temp{}'.format(uuid.uuid1()))
|
||||
buf = ArrayVariable(
|
||||
index_bits=256, index_max=32, value_bits=8, name="temp{}".format(uuid.uuid1())
|
||||
)
|
||||
value = Operators.SEXTEND(value, value.size, size * 8)
|
||||
buf = ArrayProxy(buf.write_BE(padding, value, size))
|
||||
else:
|
||||
|
||||
@@ -8,37 +8,37 @@ import ply.yacc as yacc
|
||||
# ------------------------------------------------------------
|
||||
import ply.lex as lex
|
||||
import re
|
||||
|
||||
# List of token names. This is always required
|
||||
tokens = (
|
||||
'COMMA',
|
||||
'LPAREN',
|
||||
'RPAREN',
|
||||
'LBRAKET',
|
||||
'RBRAKET',
|
||||
'NUMBER',
|
||||
|
||||
'UINTN',
|
||||
'INTN',
|
||||
'UINT',
|
||||
'INT',
|
||||
'BOOL',
|
||||
'FIXEDMN',
|
||||
'UFIXEDMN',
|
||||
'ADDRESS',
|
||||
'FIXED',
|
||||
'UFIXED',
|
||||
'FUNCTION',
|
||||
'BYTESM',
|
||||
'BYTES',
|
||||
'STRING',
|
||||
"COMMA",
|
||||
"LPAREN",
|
||||
"RPAREN",
|
||||
"LBRAKET",
|
||||
"RBRAKET",
|
||||
"NUMBER",
|
||||
"UINTN",
|
||||
"INTN",
|
||||
"UINT",
|
||||
"INT",
|
||||
"BOOL",
|
||||
"FIXEDMN",
|
||||
"UFIXEDMN",
|
||||
"ADDRESS",
|
||||
"FIXED",
|
||||
"UFIXED",
|
||||
"FUNCTION",
|
||||
"BYTESM",
|
||||
"BYTES",
|
||||
"STRING",
|
||||
)
|
||||
|
||||
# Regular expression rules for simple tokens
|
||||
t_LPAREN = r'\('
|
||||
t_RPAREN = r'\)'
|
||||
t_LBRAKET = r'\['
|
||||
t_RBRAKET = r'\]'
|
||||
t_COMMA = r'\,'
|
||||
t_LPAREN = r"\("
|
||||
t_RPAREN = r"\)"
|
||||
t_LBRAKET = r"\["
|
||||
t_RBRAKET = r"\]"
|
||||
t_COMMA = r"\,"
|
||||
|
||||
|
||||
def t_NUMBER(t):
|
||||
@@ -50,92 +50,92 @@ def t_NUMBER(t):
|
||||
# A regular expression rule with some action code
|
||||
def t_UINTN(t):
|
||||
r"uint(?P<size>256|248|240|232|224|216|208|200|192|184|176|168|160|152|144|136|128|120|112|104|96|88|80|72|64|56|48|40|32|24|16|8)"
|
||||
size = int(t.lexer.lexmatch.group('size'))
|
||||
t.value = ('uint', size)
|
||||
size = int(t.lexer.lexmatch.group("size"))
|
||||
t.value = ("uint", size)
|
||||
return t
|
||||
|
||||
|
||||
def t_ADDRESS(t):
|
||||
r"address"
|
||||
t.value = ('uint', 160)
|
||||
t.value = ("uint", 160)
|
||||
return t
|
||||
|
||||
|
||||
def t_BOOL(t):
|
||||
r"bool"
|
||||
t.value = ('uint', 8)
|
||||
t.value = ("uint", 8)
|
||||
return t
|
||||
|
||||
|
||||
def t_UINT(t):
|
||||
r"uint"
|
||||
t.value = ('uint', 256)
|
||||
t.value = ("uint", 256)
|
||||
return t
|
||||
|
||||
|
||||
def t_INTN(t):
|
||||
r"int(?P<size>256|248|240|232|224|216|208|200|192|184|176|168|160|152|144|136|128|120|112|104|96|88|80|72|64|56|48|40|32|24|16|8)"
|
||||
size = int(t.lexer.lexmatch.group('size'))
|
||||
t.value = ('int', size)
|
||||
size = int(t.lexer.lexmatch.group("size"))
|
||||
t.value = ("int", size)
|
||||
return t
|
||||
|
||||
|
||||
def t_INT(t):
|
||||
r"int"
|
||||
t.value = ('int', 256)
|
||||
t.value = ("int", 256)
|
||||
return t
|
||||
|
||||
|
||||
def t_FIXEDMN(t):
|
||||
r"^fixed(?P<M>256|248|240|232|224|216|208|200|192|184|176|168|160|152|144|136|128|120|112|104|96|88|80|72|64|56|48|40|32|24|16|8)x(?P<N>80|79|78|77|76|75|74|73|72|71|70|69|68|67|66|65|64|63|62|61|60|59|58|57|56|55|54|53|52|51|50|49|48|47|46|45|44|43|42|41|40|39|38|37|36|35|34|33|32|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1)"
|
||||
M = int(t.lexer.lexmatch.group('M'))
|
||||
N = int(t.lexer.lexmatch.group('N'))
|
||||
M = int(t.lexer.lexmatch.group("M"))
|
||||
N = int(t.lexer.lexmatch.group("N"))
|
||||
t.value = ("fixed", M, N)
|
||||
return t
|
||||
|
||||
|
||||
def t_FIXED(t):
|
||||
r"fixed"
|
||||
t.value = ('fixed', 128, 18)
|
||||
t.value = ("fixed", 128, 18)
|
||||
return t
|
||||
|
||||
|
||||
def t_UFIXEDMN(t):
|
||||
r"ufixed(?P<M>256|248|240|232|224|216|208|200|192|184|176|168|160|152|144|136|128|120|112|104|96|88|80|72|64|56|48|40|32|24|16|8)x(?P<N>80|79|78|77|76|75|74|73|72|71|70|69|68|67|66|65|64|63|62|61|60|59|58|57|56|55|54|53|52|51|50|49|48|47|46|45|44|43|42|41|40|39|38|37|36|35|34|33|32|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1)"
|
||||
M = int(t.lexer.lexmatch.group('M'))
|
||||
N = int(t.lexer.lexmatch.group('N'))
|
||||
M = int(t.lexer.lexmatch.group("M"))
|
||||
N = int(t.lexer.lexmatch.group("N"))
|
||||
t.value = ("ufixed", M, N)
|
||||
return t
|
||||
|
||||
|
||||
def t_UFIXED(t):
|
||||
r"ufixed"
|
||||
t.value = ('ufixed', 128, 18)
|
||||
t.value = ("ufixed", 128, 18)
|
||||
return t
|
||||
|
||||
|
||||
def t_BYTESM(t):
|
||||
r"bytes(?P<nbytes>32|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1)"
|
||||
size = int(t.lexer.lexmatch.group('nbytes'))
|
||||
t.value = ('bytesM', size)
|
||||
size = int(t.lexer.lexmatch.group("nbytes"))
|
||||
t.value = ("bytesM", size)
|
||||
return t
|
||||
|
||||
|
||||
def t_BYTES(t):
|
||||
r"bytes"
|
||||
t.value = ('bytes',)
|
||||
t.value = ("bytes",)
|
||||
return t
|
||||
|
||||
|
||||
def t_STRING(t):
|
||||
r"string"
|
||||
t.value = ('string',)
|
||||
t.value = ("string",)
|
||||
return t
|
||||
|
||||
|
||||
def t_FUNCTION(t):
|
||||
r"function"
|
||||
t.value = ('function',)
|
||||
t.value = ("function",)
|
||||
return t
|
||||
|
||||
|
||||
@@ -188,14 +188,14 @@ def p_tuple(p):
|
||||
"""
|
||||
T : LPAREN TL RPAREN
|
||||
"""
|
||||
p[0] = ('tuple', p[2])
|
||||
p[0] = ("tuple", p[2])
|
||||
|
||||
|
||||
def p_tuple_empty(p):
|
||||
"""
|
||||
T : LPAREN RPAREN
|
||||
"""
|
||||
p[0] = ('tuple', ())
|
||||
p[0] = ("tuple", ())
|
||||
|
||||
|
||||
def p_dynamic_type(p):
|
||||
@@ -204,7 +204,7 @@ def p_dynamic_type(p):
|
||||
"""
|
||||
reps = None
|
||||
base_type = p[1]
|
||||
p[0] = ('array', reps, base_type)
|
||||
p[0] = ("array", reps, base_type)
|
||||
|
||||
|
||||
def p_dynamic_fixed_type(p):
|
||||
@@ -213,26 +213,26 @@ def p_dynamic_fixed_type(p):
|
||||
"""
|
||||
reps = int(p[3])
|
||||
base_type = p[1]
|
||||
p[0] = ('array', reps, base_type)
|
||||
p[0] = ("array", reps, base_type)
|
||||
|
||||
|
||||
def p_error(p):
|
||||
raise Exception("Syntax Error at abitypes")
|
||||
#print(f"Syntax error at offset {lexer.lexpos:d}")
|
||||
# print(f"Syntax error at offset {lexer.lexpos:d}")
|
||||
|
||||
|
||||
with warnings.catch_warnings():
|
||||
# yacc.yacc() doesn't close the debuglog file after generating the parser table.
|
||||
warnings.simplefilter('ignore', category='ResourceWarning')
|
||||
warnings.simplefilter("ignore", category="ResourceWarning")
|
||||
parser = yacc.yacc(debug=False)
|
||||
parse = parser.parse
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
#(((((((address,string,bytes)[1],int256)[0],bytes8[1])[],((address,string,bytes)[1],int256))[])[])[4])
|
||||
if __name__ == "__main__":
|
||||
# (((((((address,string,bytes)[1],int256)[0],bytes8[1])[],((address,string,bytes)[1],int256))[])[])[4])
|
||||
while True:
|
||||
try:
|
||||
s = input('abitype > ') # use input() on Python 3
|
||||
s = input("abitype > ") # use input() on Python 3
|
||||
except EOFError:
|
||||
break
|
||||
print("R:", parser.parse(s, debug=True, tracking=True))
|
||||
|
||||
@@ -5,7 +5,7 @@ from .abi import ABI
|
||||
from ..exceptions import EthereumError
|
||||
|
||||
|
||||
HashesEntry = namedtuple('HashesEntry', 'signature func_id')
|
||||
HashesEntry = namedtuple("HashesEntry", "signature func_id")
|
||||
|
||||
|
||||
class EVMAccount:
|
||||
@@ -54,6 +54,7 @@ class EVMContract(EVMAccount):
|
||||
Note: The private methods of this class begin with a double underscore to avoid function
|
||||
name collisions with Solidity functions that begin with a single underscore.
|
||||
"""
|
||||
|
||||
def __init__(self, default_caller=None, **kwargs):
|
||||
"""
|
||||
Encapsulates a contract account.
|
||||
@@ -67,9 +68,9 @@ class EVMContract(EVMAccount):
|
||||
|
||||
def add_function(self, signature):
|
||||
func_id = ABI.function_selector(signature)
|
||||
func_name = str(signature.split('(')[0])
|
||||
func_name = str(signature.split("(")[0])
|
||||
|
||||
if func_name in self.__dict__ or func_name in {'add_function', 'address', 'name_'}:
|
||||
if func_name in self.__dict__ or func_name in {"add_function", "address", "name_"}:
|
||||
raise EthereumError(f"Function name ({func_name}) is internally reserved")
|
||||
|
||||
entry = HashesEntry(signature, func_id)
|
||||
@@ -104,23 +105,37 @@ class EVMContract(EVMAccount):
|
||||
self.__init_hashes()
|
||||
|
||||
if name in self.__hashes:
|
||||
def f(*args, signature: Optional[str]=None, caller=None, value=0, gas=0xffffffffffff, **kwargs):
|
||||
|
||||
def f(
|
||||
*args,
|
||||
signature: Optional[str] = None,
|
||||
caller=None,
|
||||
value=0,
|
||||
gas=0xFFFFFFFFFFFF,
|
||||
**kwargs,
|
||||
):
|
||||
try:
|
||||
if signature:
|
||||
if f'{name}{signature}' not in {entry.signature for entries in self.__hashes.values() for entry in entries}:
|
||||
if f"{name}{signature}" not in {
|
||||
entry.signature
|
||||
for entries in self.__hashes.values()
|
||||
for entry in entries
|
||||
}:
|
||||
raise EthereumError(
|
||||
f'Function: `{name}` has no such signature\n'
|
||||
f'Known signatures: {[entry.signature[len(name):] for entry in self.__hashes[name]]}')
|
||||
f"Function: `{name}` has no such signature\n"
|
||||
f"Known signatures: {[entry.signature[len(name):] for entry in self.__hashes[name]]}"
|
||||
)
|
||||
|
||||
tx_data = ABI.function_call(f'{name}{signature}', *args)
|
||||
tx_data = ABI.function_call(f"{name}{signature}", *args)
|
||||
else:
|
||||
entries = self.__hashes[name]
|
||||
if len(entries) > 1:
|
||||
sig = entries[0].signature[len(name):]
|
||||
sig = entries[0].signature[len(name) :]
|
||||
raise EthereumError(
|
||||
f'Function: `{name}` has multiple signatures but `signature` is not '
|
||||
f"Function: `{name}` has multiple signatures but `signature` is not "
|
||||
f'defined! Example: `account.{name}(..., signature="{sig}")`\n'
|
||||
f'Known signatures: {[entry.signature[len(name):] for entry in self.__hashes[name]]}')
|
||||
f"Known signatures: {[entry.signature[len(name):] for entry in self.__hashes[name]]}"
|
||||
)
|
||||
|
||||
tx_data = ABI.function_call(str(entries[0].signature), *args)
|
||||
except KeyError as e:
|
||||
@@ -129,11 +144,10 @@ class EVMContract(EVMAccount):
|
||||
if caller is None:
|
||||
caller = self.__default_caller
|
||||
|
||||
self._manticore.transaction(caller=caller,
|
||||
address=self._address,
|
||||
value=value,
|
||||
data=tx_data,
|
||||
gas=gas)
|
||||
self._manticore.transaction(
|
||||
caller=caller, address=self._address, value=value, data=tx_data, gas=gas
|
||||
)
|
||||
|
||||
return f
|
||||
|
||||
raise AttributeError(f"The contract {self._name} doesn't have {name} function.")
|
||||
|
||||
+46
-17
@@ -1,22 +1,41 @@
|
||||
from .detectors import DetectInvalid, DetectIntegerOverflow, DetectUninitializedStorage, \
|
||||
DetectUninitializedMemory, DetectReentrancySimple, DetectReentrancyAdvanced, \
|
||||
DetectUnusedRetVal, DetectSuicidal, DetectDelegatecall, \
|
||||
DetectExternalCallAndLeak, DetectEnvInstruction, DetectRaceCondition, DetectorClassification
|
||||
from .detectors import (
|
||||
DetectInvalid,
|
||||
DetectIntegerOverflow,
|
||||
DetectUninitializedStorage,
|
||||
DetectUninitializedMemory,
|
||||
DetectReentrancySimple,
|
||||
DetectReentrancyAdvanced,
|
||||
DetectUnusedRetVal,
|
||||
DetectSuicidal,
|
||||
DetectDelegatecall,
|
||||
DetectExternalCallAndLeak,
|
||||
DetectEnvInstruction,
|
||||
DetectRaceCondition,
|
||||
DetectorClassification,
|
||||
)
|
||||
from ..core.plugin import Profiler
|
||||
from .manticore import ManticoreEVM
|
||||
from .plugins import FilterFunctions, LoopDepthLimiter, VerboseTrace
|
||||
from ..utils.nointerrupt import WithKeyboardInterruptAs
|
||||
from ..utils import config
|
||||
|
||||
consts = config.get_group('cli')
|
||||
consts.add('profile', default=False, description='Enable worker profiling mode')
|
||||
consts = config.get_group("cli")
|
||||
consts.add("profile", default=False, description="Enable worker profiling mode")
|
||||
|
||||
|
||||
def get_detectors_classes():
|
||||
return [
|
||||
DetectInvalid, DetectIntegerOverflow, DetectUninitializedStorage, DetectUninitializedMemory,
|
||||
DetectReentrancySimple, DetectReentrancyAdvanced, DetectUnusedRetVal, DetectSuicidal, DetectDelegatecall,
|
||||
DetectExternalCallAndLeak, DetectEnvInstruction,
|
||||
DetectInvalid,
|
||||
DetectIntegerOverflow,
|
||||
DetectUninitializedStorage,
|
||||
DetectUninitializedMemory,
|
||||
DetectReentrancySimple,
|
||||
DetectReentrancyAdvanced,
|
||||
DetectUnusedRetVal,
|
||||
DetectSuicidal,
|
||||
DetectDelegatecall,
|
||||
DetectExternalCallAndLeak,
|
||||
DetectEnvInstruction,
|
||||
# The RaceCondition detector has been disabled for now as it seems to collide with IntegerOverflow detector
|
||||
# DetectRaceCondition
|
||||
]
|
||||
@@ -33,11 +52,13 @@ def choose_detectors(args):
|
||||
exclude = []
|
||||
|
||||
if args.detectors_to_exclude:
|
||||
exclude = args.detectors_to_exclude.split(',')
|
||||
exclude = args.detectors_to_exclude.split(",")
|
||||
|
||||
for e in exclude:
|
||||
if e not in arguments:
|
||||
raise Exception(f'{e} is not a detector name, must be one of {arguments}. See also `--list-detectors`.')
|
||||
raise Exception(
|
||||
f"{e} is not a detector name, must be one of {arguments}. See also `--list-detectors`."
|
||||
)
|
||||
|
||||
for arg, detector_cls in detectors.items():
|
||||
if arg not in exclude:
|
||||
@@ -65,18 +86,26 @@ def ethereum_main(args, logger):
|
||||
|
||||
if args.avoid_constant:
|
||||
# avoid all human level tx that has no effect on the storage
|
||||
filter_nohuman_constants = FilterFunctions(regexp=r".*", depth='human', mutability='constant', include=False)
|
||||
filter_nohuman_constants = FilterFunctions(
|
||||
regexp=r".*", depth="human", mutability="constant", include=False
|
||||
)
|
||||
m.register_plugin(filter_nohuman_constants)
|
||||
|
||||
if m.plugins:
|
||||
logger.info(f'Registered plugins: {", ".join(d.name for d in m.plugins)}')
|
||||
|
||||
logger.info('Beginning analysis')
|
||||
logger.info("Beginning analysis")
|
||||
|
||||
with m.kill_timeout():
|
||||
m.multi_tx_analysis(args.argv[0], contract_name=args.contract, tx_limit=args.txlimit,
|
||||
tx_use_coverage=not args.txnocoverage, tx_send_ether=not args.txnoether,
|
||||
tx_account=args.txaccount, tx_preconstrain=args.txpreconstrain)
|
||||
m.multi_tx_analysis(
|
||||
args.argv[0],
|
||||
contract_name=args.contract,
|
||||
tx_limit=args.txlimit,
|
||||
tx_use_coverage=not args.txnocoverage,
|
||||
tx_send_ether=not args.txnoether,
|
||||
tx_account=args.txaccount,
|
||||
tx_preconstrain=args.txpreconstrain,
|
||||
)
|
||||
|
||||
if not args.no_testcases:
|
||||
m.finalize()
|
||||
@@ -84,7 +113,7 @@ def ethereum_main(args, logger):
|
||||
m.kill()
|
||||
|
||||
if consts.profile:
|
||||
with open("profiling.bin", 'wb') as f:
|
||||
with open("profiling.bin", "wb") as f:
|
||||
profiler.save_profiling_data(f)
|
||||
|
||||
for detector in list(m.detectors):
|
||||
|
||||
+142
-85
@@ -16,6 +16,7 @@ class DetectorClassification:
|
||||
Shall be consistent with
|
||||
https://github.com/trailofbits/slither/blob/563d5118298e4cae7f0ea5f2a531f0dcdcebd64d/slither/detectors/abstract_detector.py#L11-L15
|
||||
"""
|
||||
|
||||
HIGH = 0
|
||||
MEDIUM = 1
|
||||
LOW = 2
|
||||
@@ -30,14 +31,16 @@ class Detector(Plugin):
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.__class__.__name__.split('.')[-1]
|
||||
return self.__class__.__name__.split(".")[-1]
|
||||
|
||||
def get_findings(self, state):
|
||||
return state.context.setdefault('{:s}.findings'.format(self.name), list())
|
||||
return state.context.setdefault("{:s}.findings".format(self.name), list())
|
||||
|
||||
@contextmanager
|
||||
def locked_global_findings(self):
|
||||
with self.manticore.locked_context('{:s}.global_findings'.format(self.name), list) as global_findings:
|
||||
with self.manticore.locked_context(
|
||||
"{:s}.global_findings".format(self.name), list
|
||||
) as global_findings:
|
||||
yield global_findings
|
||||
|
||||
@property
|
||||
@@ -65,7 +68,7 @@ class Detector(Plugin):
|
||||
self.get_findings(state).append((address, pc, finding, at_init, constraint))
|
||||
with self.locked_global_findings() as gf:
|
||||
gf.append((address, pc, finding, at_init))
|
||||
#Fixme for ever broken logger
|
||||
# Fixme for ever broken logger
|
||||
logger.warning(finding)
|
||||
|
||||
def add_finding_here(self, state, finding, constraint=True):
|
||||
@@ -77,7 +80,7 @@ class Detector(Plugin):
|
||||
"""
|
||||
address = state.platform.current_vm.address
|
||||
pc = state.platform.current_vm.pc
|
||||
at_init = state.platform.current_transaction.sort == 'CREATE'
|
||||
at_init = state.platform.current_transaction.sort == "CREATE"
|
||||
self.add_finding(state, address, pc, finding, at_init, constraint)
|
||||
|
||||
def _save_current_location(self, state, finding, condition=True):
|
||||
@@ -91,10 +94,10 @@ class Detector(Plugin):
|
||||
"""
|
||||
address = state.platform.current_vm.address
|
||||
pc = state.platform.current_vm.pc
|
||||
at_init = state.platform.current_transaction.sort == 'CREATE'
|
||||
at_init = state.platform.current_transaction.sort == "CREATE"
|
||||
location = (address, pc, finding, at_init, condition)
|
||||
hash_id = hashlib.sha1(str(location).encode()).hexdigest()
|
||||
state.context.setdefault('{:s}.locations'.format(self.name), {})[hash_id] = location
|
||||
state.context.setdefault("{:s}.locations".format(self.name), {})[hash_id] = location
|
||||
return hash_id
|
||||
|
||||
def _get_location(self, state, hash_id):
|
||||
@@ -102,7 +105,7 @@ class Detector(Plugin):
|
||||
Get previously saved location
|
||||
A location is composed of: address, pc, finding, at_init, condition
|
||||
"""
|
||||
return state.context.setdefault('{:s}.locations'.format(self.name), {})[hash_id]
|
||||
return state.context.setdefault("{:s}.locations".format(self.name), {})[hash_id]
|
||||
|
||||
def _get_src(self, address, pc):
|
||||
return self.manticore.get_metadata(address).get_source_for(pc)
|
||||
@@ -117,45 +120,59 @@ class DetectEnvInstruction(Detector):
|
||||
using it. Unless special situations. Notably to programatically detect human transactions
|
||||
`sender == origin`
|
||||
"""
|
||||
ARGUMENT = 'env-instr'
|
||||
HELP = 'Use of potentially unsafe/manipulable instructions'
|
||||
|
||||
ARGUMENT = "env-instr"
|
||||
HELP = "Use of potentially unsafe/manipulable instructions"
|
||||
IMPACT = DetectorClassification.MEDIUM
|
||||
CONFIDENCE = DetectorClassification.HIGH
|
||||
|
||||
def will_evm_execute_instruction_callback(self, state, instruction, arguments):
|
||||
if instruction.semantics in ('BLOCKHASH', 'COINBASE', 'TIMESTAMP', 'NUMBER', 'DIFFICULTY', 'GASLIMIT', 'ORIGIN', 'GASPRICE'):
|
||||
self.add_finding_here(state, f'Warning {instruction.semantics} instruction used')
|
||||
if instruction.semantics in (
|
||||
"BLOCKHASH",
|
||||
"COINBASE",
|
||||
"TIMESTAMP",
|
||||
"NUMBER",
|
||||
"DIFFICULTY",
|
||||
"GASLIMIT",
|
||||
"ORIGIN",
|
||||
"GASPRICE",
|
||||
):
|
||||
self.add_finding_here(state, f"Warning {instruction.semantics} instruction used")
|
||||
|
||||
|
||||
class DetectSuicidal(Detector):
|
||||
ARGUMENT = 'suicidal'
|
||||
HELP = 'Reachable selfdestruct instructions'
|
||||
ARGUMENT = "suicidal"
|
||||
HELP = "Reachable selfdestruct instructions"
|
||||
IMPACT = DetectorClassification.MEDIUM
|
||||
CONFIDENCE = DetectorClassification.HIGH
|
||||
|
||||
def will_evm_execute_instruction_callback(self, state, instruction, arguments):
|
||||
if instruction.semantics == 'SELFDESTRUCT':
|
||||
self.add_finding_here(state, 'Reachable SELFDESTRUCT')
|
||||
if instruction.semantics == "SELFDESTRUCT":
|
||||
self.add_finding_here(state, "Reachable SELFDESTRUCT")
|
||||
|
||||
|
||||
class DetectExternalCallAndLeak(Detector):
|
||||
ARGUMENT = 'ext-call-leak'
|
||||
HELP = 'Reachable external call or ether leak to sender or arbitrary address'
|
||||
ARGUMENT = "ext-call-leak"
|
||||
HELP = "Reachable external call or ether leak to sender or arbitrary address"
|
||||
IMPACT = DetectorClassification.MEDIUM
|
||||
CONFIDENCE = DetectorClassification.HIGH
|
||||
|
||||
def will_evm_execute_instruction_callback(self, state, instruction, arguments):
|
||||
if instruction.semantics == 'CALL':
|
||||
if instruction.semantics == "CALL":
|
||||
dest_address = arguments[1]
|
||||
sent_value = arguments[2]
|
||||
msg_sender = state.platform.current_vm.caller
|
||||
|
||||
msg = 'ether leak' if state.can_be_true(sent_value != 0) else 'external call'
|
||||
msg = "ether leak" if state.can_be_true(sent_value != 0) else "external call"
|
||||
|
||||
if issymbolic(dest_address):
|
||||
# We assume dest_address is symbolic because it came from symbolic tx data (user input argument)
|
||||
if state.can_be_true(msg_sender == dest_address):
|
||||
self.add_finding_here(state, f"Reachable {msg} to sender via argument", constraint=msg_sender == dest_address)
|
||||
self.add_finding_here(
|
||||
state,
|
||||
f"Reachable {msg} to sender via argument",
|
||||
constraint=msg_sender == dest_address,
|
||||
)
|
||||
else:
|
||||
# ok it can't go to the sender, but can it go to arbitrary addresses? (> 1 other address?)
|
||||
# we report nothing if it can't go to > 1 other addresses since that means the code constrained
|
||||
@@ -166,15 +183,19 @@ class DetectExternalCallAndLeak(Detector):
|
||||
if len(possible_destinations) > 1:
|
||||
# This might be a false positive if the dest_address can't actually be solved to anything
|
||||
# useful/exploitable, even though it can be solved to more than 1 thing
|
||||
self.add_finding_here(state, f"Reachable {msg} to user controlled address via argument", constraint=msg_sender != dest_address)
|
||||
self.add_finding_here(
|
||||
state,
|
||||
f"Reachable {msg} to user controlled address via argument",
|
||||
constraint=msg_sender != dest_address,
|
||||
)
|
||||
else:
|
||||
if msg_sender == dest_address:
|
||||
self.add_finding_here(state, f"Reachable {msg} to sender")
|
||||
|
||||
|
||||
class DetectInvalid(Detector):
|
||||
ARGUMENT = 'invalid'
|
||||
HELP = 'Enable INVALID instruction detection'
|
||||
ARGUMENT = "invalid"
|
||||
HELP = "Enable INVALID instruction detection"
|
||||
IMPACT = DetectorClassification.LOW
|
||||
CONFIDENCE = DetectorClassification.HIGH
|
||||
|
||||
@@ -194,7 +215,7 @@ class DetectInvalid(Detector):
|
||||
def will_evm_execute_instruction_callback(self, state, instruction, arguments):
|
||||
mnemonic = instruction.semantics
|
||||
|
||||
if mnemonic == 'INVALID':
|
||||
if mnemonic == "INVALID":
|
||||
if not self._only_human or state.platform.current_transaction.depth == 0:
|
||||
self.add_finding_here(state, "INVALID instruction")
|
||||
|
||||
@@ -205,21 +226,22 @@ class DetectReentrancySimple(Detector):
|
||||
Alert if contract changes the state of storage (does a write) after a call with >2300 gas to a user controlled/symbolic
|
||||
external address or the msg.sender address.
|
||||
"""
|
||||
ARGUMENT = 'reentrancy'
|
||||
HELP = 'Reentrancy bug'
|
||||
|
||||
ARGUMENT = "reentrancy"
|
||||
HELP = "Reentrancy bug"
|
||||
IMPACT = DetectorClassification.HIGH
|
||||
CONFIDENCE = DetectorClassification.HIGH
|
||||
|
||||
@property
|
||||
def _context_key(self):
|
||||
return f'{self.name}.call_locations'
|
||||
return f"{self.name}.call_locations"
|
||||
|
||||
def will_open_transaction_callback(self, state, tx):
|
||||
if tx.is_human:
|
||||
state.context[self._context_key] = []
|
||||
|
||||
def will_evm_execute_instruction_callback(self, state, instruction, arguments):
|
||||
if instruction.semantics == 'CALL':
|
||||
if instruction.semantics == "CALL":
|
||||
gas = arguments[0]
|
||||
dest_address = arguments[1]
|
||||
msg_sender = state.platform.current_vm.caller
|
||||
@@ -241,8 +263,15 @@ class DetectReentrancySimple(Detector):
|
||||
# encountered a dangerous call and is now at a write.
|
||||
for callpc, gas_constraint in locs:
|
||||
addr = state.platform.current_vm.address
|
||||
at_init = state.platform.current_transaction.sort == 'CREATE'
|
||||
self.add_finding(state, addr, callpc, 'Potential reentrancy vulnerability', at_init, constraint=gas_constraint)
|
||||
at_init = state.platform.current_transaction.sort == "CREATE"
|
||||
self.add_finding(
|
||||
state,
|
||||
addr,
|
||||
callpc,
|
||||
"Potential reentrancy vulnerability",
|
||||
at_init,
|
||||
constraint=gas_constraint,
|
||||
)
|
||||
|
||||
|
||||
class DetectReentrancyAdvanced(Detector):
|
||||
@@ -257,8 +286,9 @@ class DetectReentrancyAdvanced(Detector):
|
||||
|
||||
3) The storage slot of the SSTORE must be used in some path to control flow
|
||||
"""
|
||||
ARGUMENT = 'reentrancy-adv'
|
||||
HELP = 'Reentrancy bug (different method)'
|
||||
|
||||
ARGUMENT = "reentrancy-adv"
|
||||
HELP = "Reentrancy bug (different method)"
|
||||
IMPACT = DetectorClassification.HIGH
|
||||
CONFIDENCE = DetectorClassification.HIGH
|
||||
|
||||
@@ -271,29 +301,34 @@ class DetectReentrancyAdvanced(Detector):
|
||||
|
||||
@property
|
||||
def _read_storage_name(self):
|
||||
return '{:s}.read_storage'.format(self.name)
|
||||
return "{:s}.read_storage".format(self.name)
|
||||
|
||||
def will_open_transaction_callback(self, state, tx):
|
||||
# Reset reading log on new human transactions
|
||||
if tx.is_human:
|
||||
state.context[self._read_storage_name] = set()
|
||||
state.context['{:s}.locations'.format(self.name)] = dict()
|
||||
state.context["{:s}.locations".format(self.name)] = dict()
|
||||
|
||||
def did_close_transaction_callback(self, state, tx):
|
||||
world = state.platform
|
||||
#Check if it was an internal tx
|
||||
# Check if it was an internal tx
|
||||
if not tx.is_human:
|
||||
# Check is the tx was successful
|
||||
if tx.result:
|
||||
# Check if gas was enough for a reentrancy attack
|
||||
if tx.gas > 2300:
|
||||
# Check if target address is attaker controlled
|
||||
if self._addresses is None and not world.get_code(tx.address) or self._addresses is not None and tx.address in self._addresses:
|
||||
#that's enough. Save current location and read list
|
||||
if (
|
||||
self._addresses is None
|
||||
and not world.get_code(tx.address)
|
||||
or self._addresses is not None
|
||||
and tx.address in self._addresses
|
||||
):
|
||||
# that's enough. Save current location and read list
|
||||
self._save_location_and_reads(state)
|
||||
|
||||
def _save_location_and_reads(self, state):
|
||||
name = '{:s}.locations'.format(self.name)
|
||||
name = "{:s}.locations".format(self.name)
|
||||
locations = state.context.get(name, dict)
|
||||
world = state.platform
|
||||
address = world.current_vm.address
|
||||
@@ -301,13 +336,13 @@ class DetectReentrancyAdvanced(Detector):
|
||||
if isinstance(pc, Constant):
|
||||
pc = pc.value
|
||||
assert isinstance(pc, int)
|
||||
at_init = world.current_transaction.sort == 'CREATE'
|
||||
at_init = world.current_transaction.sort == "CREATE"
|
||||
location = (address, pc, "Reentrancy multi-million ether bug", at_init)
|
||||
locations[location] = set(state.context[self._read_storage_name])
|
||||
state.context[name] = locations
|
||||
|
||||
def _get_location_and_reads(self, state):
|
||||
name = '{:s}.locations'.format(self.name)
|
||||
name = "{:s}.locations".format(self.name)
|
||||
locations = state.context.get(name, dict)
|
||||
return locations.items()
|
||||
|
||||
@@ -328,8 +363,9 @@ class DetectIntegerOverflow(Detector):
|
||||
"""
|
||||
Detects potential overflow and underflow conditions on ADD and SUB instructions.
|
||||
"""
|
||||
ARGUMENT = 'overflow'
|
||||
HELP = 'Integer overflows'
|
||||
|
||||
ARGUMENT = "overflow"
|
||||
HELP = "Integer overflows"
|
||||
IMPACT = DetectorClassification.HIGH
|
||||
CONFIDENCE = DetectorClassification.HIGH
|
||||
|
||||
@@ -465,20 +501,20 @@ class DetectIntegerOverflow(Detector):
|
||||
ios = False
|
||||
iou = False
|
||||
|
||||
if mnemonic == 'ADD':
|
||||
if mnemonic == "ADD":
|
||||
ios = self._signed_add_overflow(state, *arguments)
|
||||
iou = self._unsigned_add_overflow(state, *arguments)
|
||||
elif mnemonic == 'MUL':
|
||||
elif mnemonic == "MUL":
|
||||
ios = self._signed_mul_overflow(state, *arguments)
|
||||
iou = self._unsigned_mul_overflow(state, *arguments)
|
||||
elif mnemonic == 'SUB':
|
||||
elif mnemonic == "SUB":
|
||||
ios = self._signed_sub_overflow(state, *arguments)
|
||||
iou = self._unsigned_sub_overflow(state, *arguments)
|
||||
elif mnemonic == 'SSTORE':
|
||||
elif mnemonic == "SSTORE":
|
||||
# If an overflowded value is stored in the storage then it is a finding
|
||||
where, what = arguments
|
||||
self._check_finding(state, what)
|
||||
elif mnemonic == 'RETURN':
|
||||
elif mnemonic == "RETURN":
|
||||
world = state.platform
|
||||
if world.current_transaction.is_human:
|
||||
# If an overflowded value is returned to a human
|
||||
@@ -486,29 +522,34 @@ class DetectIntegerOverflow(Detector):
|
||||
data = world.current_vm.read_buffer(offset, size)
|
||||
self._check_finding(state, data)
|
||||
|
||||
if mnemonic in ('SLT', 'SGT', 'SDIV', 'SMOD'):
|
||||
if mnemonic in ("SLT", "SGT", "SDIV", "SMOD"):
|
||||
result = taint_with(result, "SIGNED")
|
||||
vm.change_last_result(result)
|
||||
if state.can_be_true(ios):
|
||||
id_val = self._save_current_location(state, "Signed integer overflow at %s instruction" % mnemonic, ios)
|
||||
id_val = self._save_current_location(
|
||||
state, "Signed integer overflow at %s instruction" % mnemonic, ios
|
||||
)
|
||||
result = taint_with(result, "IOS_{:s}".format(id_val))
|
||||
vm.change_last_result(result)
|
||||
if state.can_be_true(iou):
|
||||
id_val = self._save_current_location(state, "Unsigned integer overflow at %s instruction" % mnemonic, iou)
|
||||
id_val = self._save_current_location(
|
||||
state, "Unsigned integer overflow at %s instruction" % mnemonic, iou
|
||||
)
|
||||
result = taint_with(result, "IOU_{:s}".format(id_val))
|
||||
vm.change_last_result(result)
|
||||
|
||||
|
||||
class DetectUnusedRetVal(Detector):
|
||||
"""Detects unused return value from internal transactions"""
|
||||
ARGUMENT = 'unused-return'
|
||||
HELP = 'Unused internal transaction return values'
|
||||
|
||||
ARGUMENT = "unused-return"
|
||||
HELP = "Unused internal transaction return values"
|
||||
IMPACT = DetectorClassification.LOW
|
||||
CONFIDENCE = DetectorClassification.HIGH
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._stack_name = '{:s}.stack'.format(self.name)
|
||||
self._stack_name = "{:s}.stack".format(self.name)
|
||||
|
||||
def _add_retval_taint(self, state, taint):
|
||||
taints = state.context[self._stack_name][-1]
|
||||
@@ -548,11 +589,13 @@ class DetectUnusedRetVal(Detector):
|
||||
if instruction.is_starttx:
|
||||
# A transactional instruction just returned so we add a taint to result
|
||||
# and add that taint to the set
|
||||
id_val = self._save_current_location(state, "Returned value at {:s} instruction is not used".format(mnemonic))
|
||||
id_val = self._save_current_location(
|
||||
state, "Returned value at {:s} instruction is not used".format(mnemonic)
|
||||
)
|
||||
taint = "RETVAL_{:s}".format(id_val)
|
||||
current_vm.change_last_result(taint_with(result, taint))
|
||||
self._add_retval_taint(state, taint)
|
||||
elif mnemonic == 'JUMPI':
|
||||
elif mnemonic == "JUMPI":
|
||||
dest, cond = arguments
|
||||
for used_taint in get_taints(cond, "RETVAL_.*"):
|
||||
self._remove_retval_taint(state, used_taint)
|
||||
@@ -565,8 +608,9 @@ class DetectDelegatecall(Detector):
|
||||
* the destination address can be controlled by the caller
|
||||
* the first 4 bytes of the calldata are controlled by the caller
|
||||
"""
|
||||
ARGUMENT = 'delegatecall'
|
||||
HELP = 'Problematic uses of DELEGATECALL instruction'
|
||||
|
||||
ARGUMENT = "delegatecall"
|
||||
HELP = "Problematic uses of DELEGATECALL instruction"
|
||||
IMPACT = DetectorClassification.HIGH
|
||||
CONFIDENCE = DetectorClassification.HIGH
|
||||
|
||||
@@ -579,7 +623,7 @@ class DetectDelegatecall(Detector):
|
||||
# if blockchain.last_transaction.return_value:
|
||||
# TODO: check if any of the potential target addresses has code
|
||||
# if not any( world.get_code, possible_addresses):
|
||||
if mnemonic == 'DELEGATECALL':
|
||||
if mnemonic == "DELEGATECALL":
|
||||
gas, address, in_offset, in_size, out_offset, out_size = arguments
|
||||
if issymbolic(address):
|
||||
possible_addresses = state.solve_n(address, 2)
|
||||
@@ -598,34 +642,42 @@ class DetectUninitializedMemory(Detector):
|
||||
"""
|
||||
Detects uses of uninitialized memory
|
||||
"""
|
||||
ARGUMENT = 'uninitialized-memory'
|
||||
HELP = 'Uninitialized memory usage'
|
||||
|
||||
ARGUMENT = "uninitialized-memory"
|
||||
HELP = "Uninitialized memory usage"
|
||||
IMPACT = DetectorClassification.MEDIUM
|
||||
CONFIDENCE = DetectorClassification.HIGH
|
||||
|
||||
def did_evm_read_memory_callback(self, state, offset, value):
|
||||
initialized_memory = state.context.get('{:s}.initialized_memory'.format(self.name), set())
|
||||
initialized_memory = state.context.get("{:s}.initialized_memory".format(self.name), set())
|
||||
cbu = True # Can be unknown
|
||||
current_contract = state.platform.current_vm.address
|
||||
for known_contract, known_offset in initialized_memory:
|
||||
if current_contract == known_contract:
|
||||
cbu = Operators.AND(cbu, offset != known_offset)
|
||||
if state.can_be_true(cbu):
|
||||
self.add_finding_here(state, "Potentially reading uninitialized memory at instruction (address: %r, offset %r)" % (current_contract, offset))
|
||||
self.add_finding_here(
|
||||
state,
|
||||
"Potentially reading uninitialized memory at instruction (address: %r, offset %r)"
|
||||
% (current_contract, offset),
|
||||
)
|
||||
|
||||
def did_evm_write_memory_callback(self, state, offset, value):
|
||||
current_contract = state.platform.current_vm.address
|
||||
|
||||
# concrete or symbolic write
|
||||
state.context.setdefault('{:s}.initialized_memory'.format(self.name), set()).add((current_contract, offset))
|
||||
state.context.setdefault("{:s}.initialized_memory".format(self.name), set()).add(
|
||||
(current_contract, offset)
|
||||
)
|
||||
|
||||
|
||||
class DetectUninitializedStorage(Detector):
|
||||
"""
|
||||
Detects uses of uninitialized storage
|
||||
"""
|
||||
ARGUMENT = 'uninitialized-storage'
|
||||
HELP = 'Uninitialized storage usage'
|
||||
|
||||
ARGUMENT = "uninitialized-storage"
|
||||
HELP = "Uninitialized storage usage"
|
||||
IMPACT = DetectorClassification.MEDIUM
|
||||
CONFIDENCE = DetectorClassification.HIGH
|
||||
|
||||
@@ -635,7 +687,7 @@ class DetectUninitializedStorage(Detector):
|
||||
return
|
||||
# check if offset is known
|
||||
cbu = True # Can be unknown
|
||||
context_name = '{:s}.initialized_storage'.format(self.name)
|
||||
context_name = "{:s}.initialized_storage".format(self.name)
|
||||
for known_address, known_offset in state.context.get(context_name, ()):
|
||||
cbu = Operators.AND(cbu, Operators.OR(address != known_address, offset != known_offset))
|
||||
|
||||
@@ -644,7 +696,9 @@ class DetectUninitializedStorage(Detector):
|
||||
|
||||
def did_evm_write_storage_callback(self, state, address, offset, value):
|
||||
# concrete or symbolic write
|
||||
state.context.setdefault('{:s}.initialized_storage'.format(self.name), set()).add((address, offset))
|
||||
state.context.setdefault("{:s}.initialized_storage".format(self.name), set()).add(
|
||||
(address, offset)
|
||||
)
|
||||
|
||||
|
||||
class DetectRaceCondition(Detector):
|
||||
@@ -654,12 +708,13 @@ class DetectRaceCondition(Detector):
|
||||
The RaceCondition detector might not work properly for contracts that have only a fallback function.
|
||||
See the detector's implementation and it's `_in_user_func` method for more information.
|
||||
"""
|
||||
ARGUMENT = 'race-condition'
|
||||
HELP = 'Possible transaction race conditions'
|
||||
|
||||
ARGUMENT = "race-condition"
|
||||
HELP = "Possible transaction race conditions"
|
||||
IMPACT = DetectorClassification.LOW
|
||||
CONFIDENCE = DetectorClassification.LOW
|
||||
|
||||
TAINT = 'written_storage_slots.'
|
||||
TAINT = "written_storage_slots."
|
||||
|
||||
def __init__(self, *a, **kw):
|
||||
# Normally `add_finding_here` makes it unique reporting but
|
||||
@@ -691,8 +746,8 @@ class DetectRaceCondition(Detector):
|
||||
"""
|
||||
|
||||
# If we are already in user function (we cached it) let's just return True
|
||||
in_function = state.context.get('in_function', False)
|
||||
prev_tx_count = state.context.get('prev_tx_count', 0)
|
||||
in_function = state.context.get("in_function", False)
|
||||
prev_tx_count = state.context.get("prev_tx_count", 0)
|
||||
curr_tx_count = len(state.platform.transactions)
|
||||
|
||||
new_human_tx = prev_tx_count != curr_tx_count
|
||||
@@ -703,8 +758,8 @@ class DetectRaceCondition(Detector):
|
||||
# This is expensive call, so we cache it
|
||||
in_function = len(state.solve_n(state.platform.current_transaction.data[:4], 2)) == 1
|
||||
|
||||
state.context['in_function'] = in_function
|
||||
state.context['prev_tx_count'] = curr_tx_count
|
||||
state.context["in_function"] = in_function
|
||||
state.context["prev_tx_count"] = curr_tx_count
|
||||
|
||||
return in_function
|
||||
|
||||
@@ -712,7 +767,7 @@ class DetectRaceCondition(Detector):
|
||||
world = state.platform
|
||||
curr_tx = world.current_transaction
|
||||
|
||||
if curr_tx.sort == 'CREATE' or not self._in_user_func(state):
|
||||
if curr_tx.sort == "CREATE" or not self._in_user_func(state):
|
||||
return
|
||||
|
||||
key = self.TAINT + str(offset) # offset is storage index/slot
|
||||
@@ -739,20 +794,20 @@ class DetectRaceCondition(Detector):
|
||||
world = state.platform
|
||||
curr_tx = world.current_transaction
|
||||
|
||||
if curr_tx.sort != 'CREATE':
|
||||
if curr_tx.sort != "CREATE":
|
||||
metadata = self.manticore.metadata[curr_tx.address]
|
||||
curr_func = metadata.get_func_signature(state.solve_one(curr_tx.data[:4]))
|
||||
|
||||
for arg in arguments:
|
||||
if istainted(arg):
|
||||
for taint in get_taints(arg, self.TAINT + '*'):
|
||||
tainted_val = taint[taint.rindex('.') + 1:]
|
||||
for taint in get_taints(arg, self.TAINT + "*"):
|
||||
tainted_val = taint[taint.rindex(".") + 1 :]
|
||||
|
||||
try:
|
||||
storage_index = int(tainted_val)
|
||||
storage_index_key = storage_index
|
||||
except ValueError:
|
||||
storage_index = 'which is symbolic'
|
||||
storage_index = "which is symbolic"
|
||||
storage_index_key = hash(tainted_val)
|
||||
|
||||
prev_funcs = state.context[taint]
|
||||
@@ -762,11 +817,13 @@ class DetectRaceCondition(Detector):
|
||||
if prev_func is None:
|
||||
continue
|
||||
|
||||
msg = 'Potential race condition (transaction order dependency):\n'
|
||||
msg += f'Value has been stored in storage slot/index {storage_index} in transaction that ' \
|
||||
f'called {prev_func} and is now used in transaction that calls {curr_func}.\n' \
|
||||
f'An attacker seeing a transaction to {curr_func} could create a transaction ' \
|
||||
f'to {prev_func} with high gas and win a race.'
|
||||
msg = "Potential race condition (transaction order dependency):\n"
|
||||
msg += (
|
||||
f"Value has been stored in storage slot/index {storage_index} in transaction that "
|
||||
f"called {prev_func} and is now used in transaction that calls {curr_func}.\n"
|
||||
f"An attacker seeing a transaction to {curr_func} could create a transaction "
|
||||
f"to {prev_func} with high gas and win a race."
|
||||
)
|
||||
|
||||
unique_key = (storage_index_key, prev_func, curr_func)
|
||||
if unique_key in self.__findings:
|
||||
|
||||
+478
-290
File diff suppressed because it is too large
Load Diff
@@ -1,50 +1,108 @@
|
||||
|
||||
# parsetab.py
|
||||
# This file is automatically generated. Do not edit.
|
||||
# pylint: disable=W,C,R
|
||||
_tabversion = '3.10'
|
||||
_tabversion = "3.10"
|
||||
|
||||
_lr_method = 'LALR'
|
||||
_lr_method = "LALR"
|
||||
|
||||
_lr_signature = 'ADDRESS BOOL BYTES BYTESM COMMA FIXED FIXEDMN FUNCTION INT INTN LBRAKET LPAREN NUMBER RBRAKET RPAREN STRING UFIXED UFIXEDMN UINT UINTN\n T : UINTN\n T : UINT\n T : INTN\n T : INT\n T : ADDRESS\n T : BOOL\n T : FIXEDMN\n T : UFIXEDMN\n T : FIXED\n T : UFIXED\n T : BYTESM\n T : FUNCTION\n T : BYTES\n T : STRING\n\n \n TL : T\n \n TL : T COMMA TL\n \n T : LPAREN TL RPAREN\n \n T : LPAREN RPAREN\n \n T : T LBRAKET RBRAKET\n \n T : T LBRAKET NUMBER RBRAKET\n '
|
||||
|
||||
_lr_action_items = {'UINTN':([0,16,24,],[2,2,2,]),'UINT':([0,16,24,],[3,3,3,]),'INTN':([0,16,24,],[4,4,4,]),'INT':([0,16,24,],[5,5,5,]),'ADDRESS':([0,16,24,],[6,6,6,]),'BOOL':([0,16,24,],[7,7,7,]),'FIXEDMN':([0,16,24,],[8,8,8,]),'UFIXEDMN':([0,16,24,],[9,9,9,]),'FIXED':([0,16,24,],[10,10,10,]),'UFIXED':([0,16,24,],[11,11,11,]),'BYTESM':([0,16,24,],[12,12,12,]),'FUNCTION':([0,16,24,],[13,13,13,]),'BYTES':([0,16,24,],[14,14,14,]),'STRING':([0,16,24,],[15,15,15,]),'LPAREN':([0,16,24,],[16,16,16,]),'$end':([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,19,21,23,25,],[0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-18,-19,-17,-20,]),'LBRAKET':([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,19,20,21,23,25,],[17,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-18,17,-19,-17,-20,]),'COMMA':([2,3,4,5,6,7,8,9,10,11,12,13,14,15,19,20,21,23,25,],[-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-18,24,-19,-17,-20,]),'RPAREN':([2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19,20,21,23,25,26,],[-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,19,23,-18,-15,-19,-17,-20,-16,]),'RBRAKET':([17,22,],[21,25,]),'NUMBER':([17,],[22,]),}
|
||||
_lr_signature = "ADDRESS BOOL BYTES BYTESM COMMA FIXED FIXEDMN FUNCTION INT INTN LBRAKET LPAREN NUMBER RBRAKET RPAREN STRING UFIXED UFIXEDMN UINT UINTN\n T : UINTN\n T : UINT\n T : INTN\n T : INT\n T : ADDRESS\n T : BOOL\n T : FIXEDMN\n T : UFIXEDMN\n T : FIXED\n T : UFIXED\n T : BYTESM\n T : FUNCTION\n T : BYTES\n T : STRING\n\n \n TL : T\n \n TL : T COMMA TL\n \n T : LPAREN TL RPAREN\n \n T : LPAREN RPAREN\n \n T : T LBRAKET RBRAKET\n \n T : T LBRAKET NUMBER RBRAKET\n "
|
||||
|
||||
_lr_action_items = {
|
||||
"UINTN": ([0, 16, 24], [2, 2, 2]),
|
||||
"UINT": ([0, 16, 24], [3, 3, 3]),
|
||||
"INTN": ([0, 16, 24], [4, 4, 4]),
|
||||
"INT": ([0, 16, 24], [5, 5, 5]),
|
||||
"ADDRESS": ([0, 16, 24], [6, 6, 6]),
|
||||
"BOOL": ([0, 16, 24], [7, 7, 7]),
|
||||
"FIXEDMN": ([0, 16, 24], [8, 8, 8]),
|
||||
"UFIXEDMN": ([0, 16, 24], [9, 9, 9]),
|
||||
"FIXED": ([0, 16, 24], [10, 10, 10]),
|
||||
"UFIXED": ([0, 16, 24], [11, 11, 11]),
|
||||
"BYTESM": ([0, 16, 24], [12, 12, 12]),
|
||||
"FUNCTION": ([0, 16, 24], [13, 13, 13]),
|
||||
"BYTES": ([0, 16, 24], [14, 14, 14]),
|
||||
"STRING": ([0, 16, 24], [15, 15, 15]),
|
||||
"LPAREN": ([0, 16, 24], [16, 16, 16]),
|
||||
"$end": (
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 19, 21, 23, 25],
|
||||
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -18, -19, -17, -20],
|
||||
),
|
||||
"LBRAKET": (
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 19, 20, 21, 23, 25],
|
||||
[17, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -18, 17, -19, -17, -20],
|
||||
),
|
||||
"COMMA": (
|
||||
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 19, 20, 21, 23, 25],
|
||||
[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -18, 24, -19, -17, -20],
|
||||
),
|
||||
"RPAREN": (
|
||||
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 23, 25, 26],
|
||||
[
|
||||
-1,
|
||||
-2,
|
||||
-3,
|
||||
-4,
|
||||
-5,
|
||||
-6,
|
||||
-7,
|
||||
-8,
|
||||
-9,
|
||||
-10,
|
||||
-11,
|
||||
-12,
|
||||
-13,
|
||||
-14,
|
||||
19,
|
||||
23,
|
||||
-18,
|
||||
-15,
|
||||
-19,
|
||||
-17,
|
||||
-20,
|
||||
-16,
|
||||
],
|
||||
),
|
||||
"RBRAKET": ([17, 22], [21, 25]),
|
||||
"NUMBER": ([17], [22]),
|
||||
}
|
||||
|
||||
_lr_action = {}
|
||||
for _k, _v in _lr_action_items.items():
|
||||
for _x,_y in zip(_v[0],_v[1]):
|
||||
if not _x in _lr_action: _lr_action[_x] = {}
|
||||
_lr_action[_x][_k] = _y
|
||||
for _x, _y in zip(_v[0], _v[1]):
|
||||
if not _x in _lr_action:
|
||||
_lr_action[_x] = {}
|
||||
_lr_action[_x][_k] = _y
|
||||
del _lr_action_items
|
||||
|
||||
_lr_goto_items = {'T':([0,16,24,],[1,20,20,]),'TL':([16,24,],[18,26,]),}
|
||||
_lr_goto_items = {"T": ([0, 16, 24], [1, 20, 20]), "TL": ([16, 24], [18, 26])}
|
||||
|
||||
_lr_goto = {}
|
||||
for _k, _v in _lr_goto_items.items():
|
||||
for _x, _y in zip(_v[0], _v[1]):
|
||||
if not _x in _lr_goto: _lr_goto[_x] = {}
|
||||
_lr_goto[_x][_k] = _y
|
||||
for _x, _y in zip(_v[0], _v[1]):
|
||||
if not _x in _lr_goto:
|
||||
_lr_goto[_x] = {}
|
||||
_lr_goto[_x][_k] = _y
|
||||
del _lr_goto_items
|
||||
_lr_productions = [
|
||||
("S' -> T","S'",1,None,None,None),
|
||||
('T -> UINTN','T',1,'p_basic_type','abitypes.py',154),
|
||||
('T -> UINT','T',1,'p_basic_type','abitypes.py',155),
|
||||
('T -> INTN','T',1,'p_basic_type','abitypes.py',156),
|
||||
('T -> INT','T',1,'p_basic_type','abitypes.py',157),
|
||||
('T -> ADDRESS','T',1,'p_basic_type','abitypes.py',158),
|
||||
('T -> BOOL','T',1,'p_basic_type','abitypes.py',159),
|
||||
('T -> FIXEDMN','T',1,'p_basic_type','abitypes.py',160),
|
||||
('T -> UFIXEDMN','T',1,'p_basic_type','abitypes.py',161),
|
||||
('T -> FIXED','T',1,'p_basic_type','abitypes.py',162),
|
||||
('T -> UFIXED','T',1,'p_basic_type','abitypes.py',163),
|
||||
('T -> BYTESM','T',1,'p_basic_type','abitypes.py',164),
|
||||
('T -> FUNCTION','T',1,'p_basic_type','abitypes.py',165),
|
||||
('T -> BYTES','T',1,'p_basic_type','abitypes.py',166),
|
||||
('T -> STRING','T',1,'p_basic_type','abitypes.py',167),
|
||||
('TL -> T','TL',1,'p_type_list_one','abitypes.py',175),
|
||||
('TL -> T COMMA TL','TL',3,'p_type_list','abitypes.py',182),
|
||||
('T -> LPAREN TL RPAREN','T',3,'p_tuple','abitypes.py',189),
|
||||
('T -> LPAREN RPAREN','T',2,'p_tuple_empty','abitypes.py',196),
|
||||
('T -> T LBRAKET RBRAKET','T',3,'p_dynamic_type','abitypes.py',203),
|
||||
('T -> T LBRAKET NUMBER RBRAKET','T',4,'p_dynamic_fixed_type','abitypes.py',212),
|
||||
("S' -> T", "S'", 1, None, None, None),
|
||||
("T -> UINTN", "T", 1, "p_basic_type", "abitypes.py", 154),
|
||||
("T -> UINT", "T", 1, "p_basic_type", "abitypes.py", 155),
|
||||
("T -> INTN", "T", 1, "p_basic_type", "abitypes.py", 156),
|
||||
("T -> INT", "T", 1, "p_basic_type", "abitypes.py", 157),
|
||||
("T -> ADDRESS", "T", 1, "p_basic_type", "abitypes.py", 158),
|
||||
("T -> BOOL", "T", 1, "p_basic_type", "abitypes.py", 159),
|
||||
("T -> FIXEDMN", "T", 1, "p_basic_type", "abitypes.py", 160),
|
||||
("T -> UFIXEDMN", "T", 1, "p_basic_type", "abitypes.py", 161),
|
||||
("T -> FIXED", "T", 1, "p_basic_type", "abitypes.py", 162),
|
||||
("T -> UFIXED", "T", 1, "p_basic_type", "abitypes.py", 163),
|
||||
("T -> BYTESM", "T", 1, "p_basic_type", "abitypes.py", 164),
|
||||
("T -> FUNCTION", "T", 1, "p_basic_type", "abitypes.py", 165),
|
||||
("T -> BYTES", "T", 1, "p_basic_type", "abitypes.py", 166),
|
||||
("T -> STRING", "T", 1, "p_basic_type", "abitypes.py", 167),
|
||||
("TL -> T", "TL", 1, "p_type_list_one", "abitypes.py", 175),
|
||||
("TL -> T COMMA TL", "TL", 3, "p_type_list", "abitypes.py", 182),
|
||||
("T -> LPAREN TL RPAREN", "T", 3, "p_tuple", "abitypes.py", 189),
|
||||
("T -> LPAREN RPAREN", "T", 2, "p_tuple_empty", "abitypes.py", 196),
|
||||
("T -> T LBRAKET RBRAKET", "T", 3, "p_dynamic_type", "abitypes.py", 203),
|
||||
("T -> T LBRAKET NUMBER RBRAKET", "T", 4, "p_dynamic_fixed_type", "abitypes.py", 212),
|
||||
]
|
||||
|
||||
@@ -9,7 +9,9 @@ from ..core.smtlib import Operators
|
||||
|
||||
|
||||
class FilterFunctions(Plugin):
|
||||
def __init__(self, regexp=r'.*', mutability='both', depth='both', fallback=False, include=True, **kwargs):
|
||||
def __init__(
|
||||
self, regexp=r".*", mutability="both", depth="both", fallback=False, include=True, **kwargs
|
||||
):
|
||||
"""
|
||||
Constrain input based on function metadata. Include or avoid functions selected by the specified criteria.
|
||||
|
||||
@@ -28,13 +30,13 @@ class FilterFunctions(Plugin):
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
depth = depth.lower()
|
||||
if depth not in ('human', 'internal', 'both'):
|
||||
if depth not in ("human", "internal", "both"):
|
||||
raise ValueError
|
||||
mutability = mutability.lower()
|
||||
if mutability not in ('mutable', 'constant', 'both'):
|
||||
if mutability not in ("mutable", "constant", "both"):
|
||||
raise ValueError
|
||||
|
||||
#fixme better names for member variables
|
||||
# fixme better names for member variables
|
||||
self._regexp = regexp
|
||||
self._mutability = mutability
|
||||
self._depth = depth
|
||||
@@ -45,30 +47,30 @@ class FilterFunctions(Plugin):
|
||||
world = state.platform
|
||||
tx_cnt = len(world.all_transactions)
|
||||
# Constrain input only once per tx, per plugin
|
||||
if state.context.get('constrained%d' % id(self), 0) != tx_cnt:
|
||||
state.context['constrained%d' % id(self)] = tx_cnt
|
||||
if state.context.get("constrained%d" % id(self), 0) != tx_cnt:
|
||||
state.context["constrained%d" % id(self)] = tx_cnt
|
||||
|
||||
if self._depth == 'human' and not tx.is_human:
|
||||
if self._depth == "human" and not tx.is_human:
|
||||
return
|
||||
if self._depth == 'internal' and tx.is_human:
|
||||
if self._depth == "internal" and tx.is_human:
|
||||
return
|
||||
|
||||
#Get metadata if any for the target address of current tx
|
||||
# Get metadata if any for the target address of current tx
|
||||
md = self.manticore.get_metadata(tx.address)
|
||||
if md is None:
|
||||
return
|
||||
#Let's compile the list of interesting hashes
|
||||
# Let's compile the list of interesting hashes
|
||||
selected_functions = []
|
||||
|
||||
for func_hsh in md.function_selectors:
|
||||
abi = md.get_abi(func_hsh)
|
||||
if abi['type'] == 'fallback':
|
||||
if abi["type"] == "fallback":
|
||||
continue
|
||||
if self._mutability == 'constant' and not abi.get('constant', False):
|
||||
if self._mutability == "constant" and not abi.get("constant", False):
|
||||
continue
|
||||
if self._mutability == 'mutable' and abi.get('constant', False):
|
||||
if self._mutability == "mutable" and abi.get("constant", False):
|
||||
continue
|
||||
if not re.match(self._regexp, abi['name']):
|
||||
if not re.match(self._regexp, abi["name"]):
|
||||
continue
|
||||
selected_functions.append(func_hsh)
|
||||
|
||||
@@ -80,7 +82,7 @@ class FilterFunctions(Plugin):
|
||||
constraint = reduce(Operators.OR, (tx.data[:4] == x for x in selected_functions))
|
||||
state.constrain(constraint)
|
||||
else:
|
||||
#Avoid all not selected hashes
|
||||
# Avoid all not selected hashes
|
||||
for func_hsh in md.function_selectors:
|
||||
if func_hsh in selected_functions:
|
||||
constraint = tx.data[:4] != func_hsh
|
||||
@@ -95,13 +97,17 @@ class LoopDepthLimiter(Plugin):
|
||||
self.loop_count_threshold = loop_count_threshold
|
||||
|
||||
def will_start_run_callback(self, *args):
|
||||
with self.manticore.locked_context('seen_rep', dict) as reps:
|
||||
with self.manticore.locked_context("seen_rep", dict) as reps:
|
||||
reps.clear()
|
||||
|
||||
def will_execute_instruction_callback(self, state, pc, insn):
|
||||
world = state.platform
|
||||
with self.manticore.locked_context('seen_rep', dict) as reps:
|
||||
item = (world.current_transaction.sort == 'CREATE', world.current_transaction.address, pc)
|
||||
with self.manticore.locked_context("seen_rep", dict) as reps:
|
||||
item = (
|
||||
world.current_transaction.sort == "CREATE",
|
||||
world.current_transaction.address,
|
||||
pc,
|
||||
)
|
||||
if item not in reps:
|
||||
reps[item] = 0
|
||||
reps[item] += 1
|
||||
@@ -118,14 +124,14 @@ class VerboseTrace(Plugin):
|
||||
|
||||
def will_evm_execute_instruction_callback(self, state, instruction, arguments):
|
||||
current_vm = state.platform.current_vm
|
||||
state.context.setdefault('str_trace', []).append(str(current_vm))
|
||||
state.context.setdefault("str_trace", []).append(str(current_vm))
|
||||
|
||||
def generate_testcase(self, state, testcase, message):
|
||||
trace = state.context.get('str_trace', [])
|
||||
trace = state.context.get("str_trace", [])
|
||||
|
||||
with testcase.open_stream('verbose_trace') as vt:
|
||||
with testcase.open_stream("verbose_trace") as vt:
|
||||
for t in trace:
|
||||
vt.write(t + '\n')
|
||||
vt.write(t + "\n")
|
||||
|
||||
|
||||
class VerboseTraceStdout(Plugin):
|
||||
@@ -133,5 +139,6 @@ class VerboseTraceStdout(Plugin):
|
||||
Same as VerboseTrace but prints to stdout. Note that you should use it only if Manticore
|
||||
is run with procs=1 as otherwise, the output will be clobbered.
|
||||
"""
|
||||
|
||||
def will_evm_execute_instruction_callback(self, state, instruction, arguments):
|
||||
print(state.platform.current_vm)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
from typing import Any, Dict, Mapping, Optional, Sequence, Iterable, Tuple
|
||||
import pyevmasm as EVMAsm
|
||||
|
||||
@@ -7,9 +6,10 @@ from ..utils.deprecated import deprecated
|
||||
|
||||
|
||||
class SolidityMetadata:
|
||||
|
||||
@staticmethod
|
||||
def function_signature_for_name_and_inputs(name: str, inputs: Sequence[Mapping[str, Any]]) -> str:
|
||||
def function_signature_for_name_and_inputs(
|
||||
name: str, inputs: Sequence[Mapping[str, Any]]
|
||||
) -> str:
|
||||
"""Returns the function signature for the specified name and Solidity JSON metadata inputs array.
|
||||
|
||||
The ABI specification defines the function signature as the function name followed by the parenthesised list of
|
||||
@@ -23,14 +23,25 @@ class SolidityMetadata:
|
||||
"""Equivalent to ``function_signature_for_name_and_inputs('', components)``."""
|
||||
ts = []
|
||||
for c in components:
|
||||
t: str = c['type']
|
||||
if t.startswith('tuple'):
|
||||
assert len(t) == 5 or t[5] == '['
|
||||
t = SolidityMetadata.tuple_signature_for_components(c['components']) + t[5:]
|
||||
t: str = c["type"]
|
||||
if t.startswith("tuple"):
|
||||
assert len(t) == 5 or t[5] == "["
|
||||
t = SolidityMetadata.tuple_signature_for_components(c["components"]) + t[5:]
|
||||
ts.append(t)
|
||||
return f'({",".join(ts)})'
|
||||
|
||||
def __init__(self, name, source_code, init_bytecode, runtime_bytecode, srcmap, srcmap_runtime, hashes, abi, warnings):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
source_code,
|
||||
init_bytecode,
|
||||
runtime_bytecode,
|
||||
srcmap,
|
||||
srcmap_runtime,
|
||||
hashes,
|
||||
abi,
|
||||
warnings,
|
||||
):
|
||||
""" Contract metadata for Solidity-based contracts """
|
||||
self.name = name
|
||||
if isinstance(source_code, bytes):
|
||||
@@ -39,11 +50,13 @@ class SolidityMetadata:
|
||||
self._init_bytecode = init_bytecode
|
||||
self._runtime_bytecode = runtime_bytecode
|
||||
|
||||
self._function_signatures_by_selector = {bytes.fromhex(sel): sig for sig, sel in hashes.items()}
|
||||
self._function_signatures_by_selector = {
|
||||
bytes.fromhex(sel): sig for sig, sel in hashes.items()
|
||||
}
|
||||
|
||||
fallback_selector = b'\0\0\0\0'
|
||||
fallback_selector = b"\0\0\0\0"
|
||||
while fallback_selector in self._function_signatures_by_selector:
|
||||
fallback_selector = (int.from_bytes(fallback_selector, 'big') + 1).to_bytes(4, 'big')
|
||||
fallback_selector = (int.from_bytes(fallback_selector, "big") + 1).to_bytes(4, "big")
|
||||
self._fallback_function_selector = fallback_selector
|
||||
|
||||
self._constructor_abi_item = None
|
||||
@@ -51,18 +64,24 @@ class SolidityMetadata:
|
||||
function_items = {}
|
||||
event_items = {}
|
||||
for item in abi:
|
||||
type = item['type']
|
||||
if type == 'function':
|
||||
signature = self.function_signature_for_name_and_inputs(item['name'], item['inputs'])
|
||||
type = item["type"]
|
||||
if type == "function":
|
||||
signature = self.function_signature_for_name_and_inputs(
|
||||
item["name"], item["inputs"]
|
||||
)
|
||||
function_items[signature] = item
|
||||
elif type == 'event':
|
||||
signature = self.function_signature_for_name_and_inputs(item['name'], item['inputs'])
|
||||
elif type == "event":
|
||||
signature = self.function_signature_for_name_and_inputs(
|
||||
item["name"], item["inputs"]
|
||||
)
|
||||
event_items[signature] = item
|
||||
elif type == 'constructor':
|
||||
elif type == "constructor":
|
||||
assert not self._constructor_abi_item, "A constructor cannot be overloaded"
|
||||
self._constructor_abi_item = item
|
||||
elif type == 'fallback':
|
||||
assert not self._fallback_function_abi_item, "There can only be one fallback function"
|
||||
elif type == "fallback":
|
||||
assert (
|
||||
not self._fallback_function_abi_item
|
||||
), "There can only be one fallback function"
|
||||
self._fallback_function_abi_item = item
|
||||
self._function_abi_items_by_signature = function_items
|
||||
self._event_abi_items_by_signature = event_items
|
||||
@@ -74,13 +93,15 @@ class SolidityMetadata:
|
||||
def get_constructor_arguments(self) -> str:
|
||||
"""Returns the tuple type signature for the arguments of the contract constructor."""
|
||||
item = self._constructor_abi_item
|
||||
return '()' if item is None else self.tuple_signature_for_components(item['inputs'])
|
||||
return "()" if item is None else self.tuple_signature_for_components(item["inputs"])
|
||||
|
||||
@staticmethod
|
||||
def _without_metadata(bytecode):
|
||||
end = None
|
||||
if bytecode[-43: -34] == b'\xa1\x65\x62\x7a\x7a\x72\x30\x58\x20' \
|
||||
and bytecode[-2:] == b'\x00\x29':
|
||||
if (
|
||||
bytecode[-43:-34] == b"\xa1\x65\x62\x7a\x7a\x72\x30\x58\x20"
|
||||
and bytecode[-2:] == b"\x00\x29"
|
||||
):
|
||||
end = -9 - 32 - 2 # Size of metadata at the end of most contracts
|
||||
return bytecode[:end]
|
||||
|
||||
@@ -92,11 +113,15 @@ class SolidityMetadata:
|
||||
|
||||
asm_offset = 0
|
||||
asm_pos = 0
|
||||
md = dict(enumerate(srcmap[asm_pos].split(':')))
|
||||
byte_offset = int(md.get(0, 0)) # is the byte-offset to the start of the range in the source file
|
||||
md = dict(enumerate(srcmap[asm_pos].split(":")))
|
||||
byte_offset = int(
|
||||
md.get(0, 0)
|
||||
) # is the byte-offset to the start of the range in the source file
|
||||
source_len = int(md.get(1, 0)) # is the length of the source range in bytes
|
||||
file_index = int(md.get(2, 0)) # is the source index over sourceList
|
||||
jump_type = md.get(3, None) # this can be either i, o or - signifying whether a jump instruction goes into a function, returns from a function or is a regular jump as part of e.g. a loop
|
||||
jump_type = md.get(
|
||||
3, None
|
||||
) # this can be either i, o or - signifying whether a jump instruction goes into a function, returns from a function or is a regular jump as part of e.g. a loop
|
||||
|
||||
pos_to_offset = {}
|
||||
for i in EVMAsm.disassemble_all(bytecode):
|
||||
@@ -106,14 +131,19 @@ class SolidityMetadata:
|
||||
|
||||
for asm_pos, md in enumerate(srcmap):
|
||||
if len(md):
|
||||
d = {p: k for p, k in enumerate(md.split(':')) if k}
|
||||
d = {p: k for p, k in enumerate(md.split(":")) if k}
|
||||
|
||||
byte_offset = int(d.get(0, byte_offset))
|
||||
source_len = int(d.get(1, source_len))
|
||||
file_index = int(d.get(2, file_index))
|
||||
jump_type = d.get(3, jump_type)
|
||||
|
||||
new_srcmap[pos_to_offset[asm_pos]] = (byte_offset, source_len, file_index, jump_type)
|
||||
new_srcmap[pos_to_offset[asm_pos]] = (
|
||||
byte_offset,
|
||||
source_len,
|
||||
file_index,
|
||||
jump_type,
|
||||
)
|
||||
|
||||
return new_srcmap
|
||||
|
||||
@@ -136,14 +166,14 @@ class SolidityMetadata:
|
||||
try:
|
||||
beg, size, _, _ = srcmap[asm_offset]
|
||||
except KeyError:
|
||||
#asm_offset pointing outside the known bytecode
|
||||
return ''
|
||||
# asm_offset pointing outside the known bytecode
|
||||
return ""
|
||||
|
||||
output = ''
|
||||
nl = self.source_code[:beg].count('\n') + 1
|
||||
snippet = self.source_code[beg:beg + size]
|
||||
for l in snippet.split('\n'):
|
||||
output += ' %s %s\n' % (nl, l)
|
||||
output = ""
|
||||
nl = self.source_code[:beg].count("\n") + 1
|
||||
snippet = self.source_code[beg : beg + size]
|
||||
for l in snippet.split("\n"):
|
||||
output += " %s %s\n" % (nl, l)
|
||||
nl += 1
|
||||
return output
|
||||
|
||||
@@ -172,7 +202,12 @@ class SolidityMetadata:
|
||||
item = self._constructor_abi_item
|
||||
if item:
|
||||
return dict(item)
|
||||
return {'inputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'constructor'}
|
||||
return {
|
||||
"inputs": [],
|
||||
"payable": False,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor",
|
||||
}
|
||||
|
||||
def get_abi(self, hsh: bytes) -> Dict[str, Any]:
|
||||
"""Returns a copy of the Solidity JSON ABI item for the function associated with the selector ``hsh``.
|
||||
@@ -183,7 +218,7 @@ class SolidityMetadata:
|
||||
The content of the returned dict is described at https://solidity.readthedocs.io/en/latest/abi-spec.html#json_
|
||||
"""
|
||||
if not isinstance(hsh, (bytes, bytearray)):
|
||||
raise TypeError('The selector argument must be a concrete byte array')
|
||||
raise TypeError("The selector argument must be a concrete byte array")
|
||||
sig = self._function_signatures_by_selector.get(hsh)
|
||||
if sig is not None:
|
||||
return dict(self._function_abi_items_by_signature[sig])
|
||||
@@ -191,7 +226,7 @@ class SolidityMetadata:
|
||||
if item is not None:
|
||||
return dict(item)
|
||||
# An item describing the default fallback function.
|
||||
return {'payable': False, 'stateMutability': 'nonpayable', 'type': 'fallback'}
|
||||
return {"payable": False, "stateMutability": "nonpayable", "type": "fallback"}
|
||||
|
||||
def get_func_argument_types(self, hsh: bytes):
|
||||
"""Returns the tuple type signature for the arguments of the function associated with the selector ``hsh``.
|
||||
@@ -200,9 +235,9 @@ class SolidityMetadata:
|
||||
the empty tuple type signature ``'()'`` is returned.
|
||||
"""
|
||||
if not isinstance(hsh, (bytes, bytearray)):
|
||||
raise TypeError('The selector argument must be a concrete byte array')
|
||||
raise TypeError("The selector argument must be a concrete byte array")
|
||||
sig = self._function_signatures_by_selector.get(hsh)
|
||||
return '()' if sig is None else sig[sig.find('('):]
|
||||
return "()" if sig is None else sig[sig.find("(") :]
|
||||
|
||||
def get_func_return_types(self, hsh: bytes) -> str:
|
||||
"""Returns the tuple type signature for the output values of the function
|
||||
@@ -212,19 +247,19 @@ class SolidityMetadata:
|
||||
the empty tuple type signature ``'()'`` is returned.
|
||||
"""
|
||||
if not isinstance(hsh, (bytes, bytearray)):
|
||||
raise TypeError('The selector argument must be a concrete byte array')
|
||||
raise TypeError("The selector argument must be a concrete byte array")
|
||||
abi = self.get_abi(hsh)
|
||||
outputs = abi.get('outputs')
|
||||
return '()' if outputs is None else SolidityMetadata.tuple_signature_for_components(outputs)
|
||||
outputs = abi.get("outputs")
|
||||
return "()" if outputs is None else SolidityMetadata.tuple_signature_for_components(outputs)
|
||||
|
||||
def get_func_name(self, hsh: bytes) -> str:
|
||||
"""Returns the name of the normal function with the selector ``hsh``,
|
||||
or ``'{fallback}'`` if no such function exists.
|
||||
"""
|
||||
if not isinstance(hsh, (bytes, bytearray)):
|
||||
raise TypeError('The selector argument must be a concrete byte array')
|
||||
raise TypeError("The selector argument must be a concrete byte array")
|
||||
sig = self._function_signatures_by_selector.get(hsh)
|
||||
return '{fallback}' if sig is None else sig[:sig.find('(')]
|
||||
return "{fallback}" if sig is None else sig[: sig.find("(")]
|
||||
|
||||
def get_func_signature(self, hsh: bytes) -> Optional[str]:
|
||||
"""Returns the signature of the normal function with the selector ``hsh``,
|
||||
@@ -233,7 +268,7 @@ class SolidityMetadata:
|
||||
This function returns ``None`` for any selector that will be dispatched to a fallback function.
|
||||
"""
|
||||
if not isinstance(hsh, (bytes, bytearray)):
|
||||
raise TypeError('The selector argument must be a concrete byte array')
|
||||
raise TypeError("The selector argument must be a concrete byte array")
|
||||
return self._function_signatures_by_selector.get(hsh)
|
||||
|
||||
@deprecated("Use `abi.ABI.function_selector` instead.")
|
||||
@@ -246,10 +281,12 @@ class SolidityMetadata:
|
||||
return self._function_signatures_by_selector.values()
|
||||
|
||||
@property
|
||||
@deprecated("Use `.function_signatures` instead, which does not return the `'{fallback}()'` pseudo-signature")
|
||||
@deprecated(
|
||||
"Use `.function_signatures` instead, which does not return the `'{fallback}()'` pseudo-signature"
|
||||
)
|
||||
def functions(self) -> Tuple[str, ...]:
|
||||
"""The signatures of all normal contract functions, plus the ``'{fallback}()'`` pseudo-signature."""
|
||||
return (*self._function_signatures_by_selector.values(), '{fallback}()')
|
||||
return (*self._function_signatures_by_selector.values(), "{fallback}()")
|
||||
|
||||
@property
|
||||
def has_non_default_fallback_function(self) -> bool:
|
||||
@@ -275,8 +312,10 @@ class SolidityMetadata:
|
||||
return (*selectors, self.fallback_function_selector)
|
||||
|
||||
@property
|
||||
@deprecated("Use `.function_selectors` instead, which only returns a fallback"
|
||||
" function selector if the contract has a non-default fallback function.")
|
||||
@deprecated(
|
||||
"Use `.function_selectors` instead, which only returns a fallback"
|
||||
" function selector if the contract has a non-default fallback function."
|
||||
)
|
||||
def hashes(self) -> Tuple[bytes, ...]:
|
||||
"""The selectors of all normal contract functions, plus ``self.fallback_function_selector``."""
|
||||
selectors = self._function_signatures_by_selector.keys()
|
||||
@@ -299,11 +338,11 @@ class SolidityMetadata:
|
||||
else:
|
||||
arguments = (calldata,)
|
||||
|
||||
arguments_str = ', '.join(map(str, arguments))
|
||||
arguments_str = ", ".join(map(str, arguments))
|
||||
return_value = None
|
||||
if returndata:
|
||||
ret_types = self.get_func_return_types(function_id)
|
||||
return_value = ABI.deserialize(ret_types, returndata) # function return
|
||||
return f'{function_name}({arguments_str}) -> {return_value}'
|
||||
return f"{function_name}({arguments_str}) -> {return_value}"
|
||||
else:
|
||||
return f'{function_name}({arguments_str})'
|
||||
return f"{function_name}({arguments_str})"
|
||||
|
||||
@@ -7,6 +7,7 @@ class ManticoreError(Exception):
|
||||
"""
|
||||
Top level Exception object for custom exception hierarchy
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@@ -16,6 +17,7 @@ class ExecutorError(ManticoreError):
|
||||
|
||||
# Smtlib
|
||||
|
||||
|
||||
class SmtlibError(ManticoreError):
|
||||
pass
|
||||
|
||||
@@ -47,7 +49,10 @@ class EthereumError(ManticoreError):
|
||||
|
||||
class DependencyError(EthereumError):
|
||||
def __init__(self, lib_names):
|
||||
super().__init__("You must pre-load and provide libraries addresses{ libname:address, ...} for %r" % lib_names)
|
||||
super().__init__(
|
||||
"You must pre-load and provide libraries addresses{ libname:address, ...} for %r"
|
||||
% lib_names
|
||||
)
|
||||
self.lib_names = lib_names
|
||||
|
||||
|
||||
|
||||
+11
-4
@@ -3,11 +3,18 @@ from ..core.plugin import InstructionCounter, Visited, Tracer, RecordSymbolicBra
|
||||
|
||||
|
||||
def native_main(args, _logger):
|
||||
env = {key: val for key, val in [env[0].split('=') for env in args.env]}
|
||||
env = {key: val for key, val in [env[0].split("=") for env in args.env]}
|
||||
|
||||
m = Manticore(args.argv[0], argv=args.argv[1:], env=env, entry_symbol=args.entrysymbol,
|
||||
workspace_url=args.workspace, policy=args.policy,
|
||||
concrete_start=args.data, pure_symbolic=args.pure_symbolic)
|
||||
m = Manticore(
|
||||
args.argv[0],
|
||||
argv=args.argv[1:],
|
||||
env=env,
|
||||
entry_symbol=args.entrysymbol,
|
||||
workspace_url=args.workspace,
|
||||
policy=args.policy,
|
||||
concrete_start=args.data,
|
||||
pure_symbolic=args.pure_symbolic,
|
||||
)
|
||||
|
||||
# Default plugins for now.. FIXME REMOVE!
|
||||
m.register_plugin(InstructionCounter())
|
||||
|
||||
+1196
-1235
File diff suppressed because it is too large
Load Diff
+149
-105
@@ -9,9 +9,7 @@ from itertools import islice
|
||||
import unicorn
|
||||
|
||||
from .disasm import init_disassembler
|
||||
from ..memory import (
|
||||
ConcretizeMemory, InvalidMemoryAccess, FileMap, AnonMap
|
||||
)
|
||||
from ..memory import ConcretizeMemory, InvalidMemoryAccess, FileMap, AnonMap
|
||||
from ..memory import LazySMemory
|
||||
from ...core.smtlib import Expression, BitVec, Operators, Constant
|
||||
from ...core.smtlib import visitors
|
||||
@@ -27,7 +25,7 @@ from capstone.x86 import X86_REG_ENDING
|
||||
from capstone.arm import ARM_REG_ENDING
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
register_logger = logging.getLogger(f'{__name__}.registers')
|
||||
register_logger = logging.getLogger(f"{__name__}.registers")
|
||||
|
||||
###################################################################################
|
||||
# Exceptions
|
||||
@@ -84,7 +82,7 @@ class ConcretizeRegister(CpuException):
|
||||
Raised when a symbolic register needs to be concretized.
|
||||
"""
|
||||
|
||||
def __init__(self, cpu, reg_name, message=None, policy='MINMAX'):
|
||||
def __init__(self, cpu, reg_name, message=None, policy="MINMAX"):
|
||||
self.message = message if message else f"Concretizing {reg_name}"
|
||||
|
||||
self.cpu = cpu
|
||||
@@ -97,7 +95,7 @@ class ConcretizeArgument(CpuException):
|
||||
Raised when a symbolic argument needs to be concretized.
|
||||
"""
|
||||
|
||||
def __init__(self, cpu, argnum, policy='MINMAX'):
|
||||
def __init__(self, cpu, argnum, policy="MINMAX"):
|
||||
self.message = f"Concretizing argument #{argnum}."
|
||||
self.cpu = cpu
|
||||
self.policy = policy
|
||||
@@ -111,6 +109,7 @@ class Operand:
|
||||
"""This class encapsulates how to access operands (regs/mem/immediates) for
|
||||
different CPUs
|
||||
"""
|
||||
|
||||
class MemSpec:
|
||||
"""
|
||||
Auxiliary class wraps capstone operand 'mem' attribute. This will
|
||||
@@ -119,6 +118,7 @@ class Operand:
|
||||
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
segment = property(lambda self: self.parent._reg_name(self.parent.op.mem.segment))
|
||||
base = property(lambda self: self.parent._reg_name(self.parent.op.mem.base))
|
||||
index = property(lambda self: self.parent._reg_name(self.parent.op.mem.index))
|
||||
@@ -151,13 +151,15 @@ class Operand:
|
||||
:param int reg_id: Register ID
|
||||
"""
|
||||
# XXX: Support other architectures.
|
||||
if ((self.cpu.arch == CS_ARCH_ARM64 and reg_id >= ARM64_REG_ENDING) or
|
||||
(self.cpu.arch == CS_ARCH_X86 and reg_id >= X86_REG_ENDING) or
|
||||
(self.cpu.arch == CS_ARCH_ARM and reg_id >= ARM_REG_ENDING)):
|
||||
if (
|
||||
(self.cpu.arch == CS_ARCH_ARM64 and reg_id >= ARM64_REG_ENDING)
|
||||
or (self.cpu.arch == CS_ARCH_X86 and reg_id >= X86_REG_ENDING)
|
||||
or (self.cpu.arch == CS_ARCH_ARM and reg_id >= ARM_REG_ENDING)
|
||||
):
|
||||
logger.warning("Trying to get register name for a non-register")
|
||||
return None
|
||||
cs_reg_name = self.cpu.instruction.reg_name(reg_id)
|
||||
if cs_reg_name is None or cs_reg_name.lower() == '(invalid)':
|
||||
if cs_reg_name is None or cs_reg_name.lower() == "(invalid)":
|
||||
return None
|
||||
return self.cpu._regfile._alias(cs_reg_name.upper())
|
||||
|
||||
@@ -195,6 +197,7 @@ class Operand:
|
||||
""" It writes the value of specific type to the registers or memory """
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
# Basic register file structure not actually need to abstract as it's used
|
||||
# only from the cpu implementation
|
||||
|
||||
@@ -371,7 +374,7 @@ class Abi:
|
||||
descriptors = self.get_arguments()
|
||||
src = next(islice(descriptors, idx, idx + 1))
|
||||
|
||||
msg = 'Concretizing due to model invocation'
|
||||
msg = "Concretizing due to model invocation"
|
||||
if isinstance(src, str):
|
||||
raise ConcretizeRegister(self._cpu, src, msg)
|
||||
else:
|
||||
@@ -385,7 +388,7 @@ class Abi:
|
||||
return result
|
||||
|
||||
|
||||
platform_logger = logging.getLogger('manticore.platforms.platform')
|
||||
platform_logger = logging.getLogger("manticore.platforms.platform")
|
||||
|
||||
|
||||
def unsigned_hexlify(i):
|
||||
@@ -419,11 +422,14 @@ class SyscallAbi(Abi):
|
||||
# invoke() will call get_argument_values()
|
||||
self._last_arguments = ()
|
||||
|
||||
self._cpu._publish('will_execute_syscall', model)
|
||||
self._cpu._publish("will_execute_syscall", model)
|
||||
ret = super().invoke(model, prefix_args)
|
||||
self._cpu._publish('did_execute_syscall',
|
||||
model.__func__.__name__ if isinstance(model, types.MethodType) else model.__name__,
|
||||
self._last_arguments, ret)
|
||||
self._cpu._publish(
|
||||
"did_execute_syscall",
|
||||
model.__func__.__name__ if isinstance(model, types.MethodType) else model.__name__,
|
||||
self._last_arguments,
|
||||
ret,
|
||||
)
|
||||
|
||||
if platform_logger.isEnabledFor(logging.DEBUG):
|
||||
# Try to expand strings up to max_arg_expansion
|
||||
@@ -433,21 +439,23 @@ class SyscallAbi(Abi):
|
||||
|
||||
args = []
|
||||
for arg in self._last_arguments:
|
||||
arg_s = unsigned_hexlify(arg) if abs(arg) > min_hex_expansion else f'{arg}'
|
||||
if self._cpu.memory.access_ok(arg, 'r') and \
|
||||
model.__func__.__name__ not in {'sys_mprotect', 'sys_mmap'}:
|
||||
arg_s = unsigned_hexlify(arg) if abs(arg) > min_hex_expansion else f"{arg}"
|
||||
if self._cpu.memory.access_ok(arg, "r") and model.__func__.__name__ not in {
|
||||
"sys_mprotect",
|
||||
"sys_mmap",
|
||||
}:
|
||||
try:
|
||||
s = self._cpu.read_string(arg, max_arg_expansion)
|
||||
s = s.rstrip().replace('\n', '\\n') if s else s
|
||||
arg_s = (f'"{s}"' if s else arg_s)
|
||||
s = s.rstrip().replace("\n", "\\n") if s else s
|
||||
arg_s = f'"{s}"' if s else arg_s
|
||||
except Exception:
|
||||
pass
|
||||
args.append(arg_s)
|
||||
|
||||
args_s = ', '.join(args)
|
||||
ret_s = f'{unsigned_hexlify(ret)}' if abs(ret) > min_hex_expansion else f'{ret}'
|
||||
args_s = ", ".join(args)
|
||||
ret_s = f"{unsigned_hexlify(ret)}" if abs(ret) > min_hex_expansion else f"{ret}"
|
||||
|
||||
platform_logger.debug('%s(%s) = %s', model.__func__.__name__, args_s, ret_s)
|
||||
platform_logger.debug("%s(%s) = %s", model.__func__.__name__, args_s, ret_s)
|
||||
|
||||
|
||||
############################################################################
|
||||
@@ -470,13 +478,23 @@ class Cpu(Eventful):
|
||||
- stack_alias
|
||||
"""
|
||||
|
||||
_published_events = {'write_register', 'read_register', 'write_memory', 'read_memory', 'decode_instruction',
|
||||
'execute_instruction', 'set_descriptor', 'map_memory', 'protect_memory', 'unmap_memory',
|
||||
'execute_syscall'}
|
||||
_published_events = {
|
||||
"write_register",
|
||||
"read_register",
|
||||
"write_memory",
|
||||
"read_memory",
|
||||
"decode_instruction",
|
||||
"execute_instruction",
|
||||
"set_descriptor",
|
||||
"map_memory",
|
||||
"protect_memory",
|
||||
"unmap_memory",
|
||||
"execute_syscall",
|
||||
}
|
||||
|
||||
def __init__(self, regfile, memory, **kwargs):
|
||||
assert isinstance(regfile, RegisterFile)
|
||||
self._disasm = kwargs.pop("disasm", 'capstone')
|
||||
self._disasm = kwargs.pop("disasm", "capstone")
|
||||
super().__init__(**kwargs)
|
||||
self._regfile = regfile
|
||||
self._memory = memory
|
||||
@@ -489,29 +507,33 @@ class Cpu(Eventful):
|
||||
if not hasattr(self, "disasm"):
|
||||
self.disasm = init_disassembler(self._disasm, self.arch, self.mode)
|
||||
# Ensure that regfile created STACK/PC aliases
|
||||
assert 'STACK' in self._regfile
|
||||
assert 'PC' in self._regfile
|
||||
assert "STACK" in self._regfile
|
||||
assert "PC" in self._regfile
|
||||
|
||||
def __getstate__(self):
|
||||
state = super().__getstate__()
|
||||
state['regfile'] = self._regfile
|
||||
state['memory'] = self._memory
|
||||
state['icount'] = self._icount
|
||||
state['last_pc'] = self._last_pc
|
||||
state['disassembler'] = self._disasm
|
||||
state['concrete'] = self._concrete
|
||||
state['break_unicorn_at'] = self._break_unicorn_at
|
||||
state["regfile"] = self._regfile
|
||||
state["memory"] = self._memory
|
||||
state["icount"] = self._icount
|
||||
state["last_pc"] = self._last_pc
|
||||
state["disassembler"] = self._disasm
|
||||
state["concrete"] = self._concrete
|
||||
state["break_unicorn_at"] = self._break_unicorn_at
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
Cpu.__init__(self, state['regfile'],
|
||||
state['memory'],
|
||||
disasm=state['disassembler'], concrete=state['concrete'])
|
||||
self._icount = state['icount']
|
||||
self._last_pc = state['last_pc']
|
||||
self._disasm = state['disassembler']
|
||||
self._concrete = state['concrete']
|
||||
self._break_unicorn_at = state['break_unicorn_at']
|
||||
Cpu.__init__(
|
||||
self,
|
||||
state["regfile"],
|
||||
state["memory"],
|
||||
disasm=state["disassembler"],
|
||||
concrete=state["concrete"],
|
||||
)
|
||||
self._icount = state["icount"]
|
||||
self._last_pc = state["last_pc"]
|
||||
self._disasm = state["disassembler"]
|
||||
self._concrete = state["concrete"]
|
||||
self._break_unicorn_at = state["break_unicorn_at"]
|
||||
super().__setstate__(state)
|
||||
|
||||
@property
|
||||
@@ -555,9 +577,9 @@ class Cpu(Eventful):
|
||||
:param value: register value
|
||||
:type value: int or long or Expression
|
||||
"""
|
||||
self._publish('will_write_register', register, value)
|
||||
self._publish("will_write_register", register, value)
|
||||
value = self._regfile.write(register, value)
|
||||
self._publish('did_write_register', register, value)
|
||||
self._publish("did_write_register", register, value)
|
||||
return value
|
||||
|
||||
def read_register(self, register):
|
||||
@@ -568,9 +590,9 @@ class Cpu(Eventful):
|
||||
:return: register value
|
||||
:rtype: int or long or Expression
|
||||
"""
|
||||
self._publish('will_read_register', register)
|
||||
self._publish("will_read_register", register)
|
||||
value = self._regfile.read(register)
|
||||
self._publish('did_read_register', register, value)
|
||||
self._publish("did_read_register", register, value)
|
||||
return value
|
||||
|
||||
# Pythonic access to registers and aliases
|
||||
@@ -580,7 +602,7 @@ class Cpu(Eventful):
|
||||
|
||||
:param str name: Name of the register
|
||||
"""
|
||||
if name != '_regfile':
|
||||
if name != "_regfile":
|
||||
if name in self._regfile:
|
||||
return self.read_register(name)
|
||||
raise AttributeError(name)
|
||||
@@ -631,12 +653,14 @@ class Cpu(Eventful):
|
||||
if size is None:
|
||||
size = self.address_bit_size
|
||||
assert size in SANE_SIZES
|
||||
self._publish('will_write_memory', where, expression, size)
|
||||
self._publish("will_write_memory", where, expression, size)
|
||||
|
||||
data = [Operators.CHR(Operators.EXTRACT(expression, offset, 8)) for offset in range(0, size, 8)]
|
||||
data = [
|
||||
Operators.CHR(Operators.EXTRACT(expression, offset, 8)) for offset in range(0, size, 8)
|
||||
]
|
||||
self._memory.write(where, data, force)
|
||||
|
||||
self._publish('did_write_memory', where, expression, size)
|
||||
self._publish("did_write_memory", where, expression, size)
|
||||
|
||||
def _raw_read(self, where: int, size=1) -> bytes:
|
||||
"""
|
||||
@@ -653,23 +677,25 @@ class Cpu(Eventful):
|
||||
end = map._get_offset(where + size)
|
||||
|
||||
if end > map._mapped_size:
|
||||
logger.warning(f"Missing {end - map._mapped_size} bytes at the end of {map._filename}")
|
||||
logger.warning(
|
||||
f"Missing {end - map._mapped_size} bytes at the end of {map._filename}"
|
||||
)
|
||||
|
||||
raw_data = map._data[map._get_offset(where): min(end, map._mapped_size)]
|
||||
raw_data = map._data[map._get_offset(where) : min(end, map._mapped_size)]
|
||||
if len(raw_data) < end:
|
||||
raw_data += b'\x00' * (end - len(raw_data))
|
||||
raw_data += b"\x00" * (end - len(raw_data))
|
||||
|
||||
data = b''
|
||||
data = b""
|
||||
for offset in sorted(map._overlay.keys()):
|
||||
data += raw_data[len(data):offset]
|
||||
data += raw_data[len(data) : offset]
|
||||
data += map._overlay[offset]
|
||||
data += raw_data[len(data):]
|
||||
data += raw_data[len(data) :]
|
||||
|
||||
elif mapType is AnonMap:
|
||||
data = bytes(map._data[start:start + size])
|
||||
data = bytes(map._data[start : start + size])
|
||||
else:
|
||||
data = b''.join(self.memory[where:where + size])
|
||||
assert len(data) == size, 'Raw read resulted in wrong data read which should never happen'
|
||||
data = b"".join(self.memory[where : where + size])
|
||||
assert len(data) == size, "Raw read resulted in wrong data read which should never happen"
|
||||
return data
|
||||
|
||||
def read_int(self, where, size=None, force=False):
|
||||
@@ -685,13 +711,13 @@ class Cpu(Eventful):
|
||||
if size is None:
|
||||
size = self.address_bit_size
|
||||
assert size in SANE_SIZES
|
||||
self._publish('will_read_memory', where, size)
|
||||
self._publish("will_read_memory", where, size)
|
||||
|
||||
data = self._memory.read(where, size // 8, force)
|
||||
assert (8 * len(data)) == size
|
||||
value = Operators.CONCAT(size, *map(Operators.ORD, reversed(data)))
|
||||
|
||||
self._publish('did_read_memory', where, value, size)
|
||||
self._publish("did_read_memory", where, value, size)
|
||||
return value
|
||||
|
||||
def write_bytes(self, where, data, force=False):
|
||||
@@ -709,19 +735,21 @@ class Cpu(Eventful):
|
||||
# At the very least, using it in non-concrete mode will break the symbolic strcmp/strlen models. The 1024 byte
|
||||
# minimum is intended to minimize the potential effects of this by ensuring that if there _are_ any other
|
||||
# issues, they'll only crop up when we're doing very large writes, which are fairly uncommon.
|
||||
can_write_raw = type(mp) is AnonMap and \
|
||||
isinstance(data, (str, bytes)) and \
|
||||
(mp.end - mp.start + 1) >= len(data) >= 1024 and \
|
||||
not issymbolic(data) and \
|
||||
self._concrete
|
||||
can_write_raw = (
|
||||
type(mp) is AnonMap
|
||||
and isinstance(data, (str, bytes))
|
||||
and (mp.end - mp.start + 1) >= len(data) >= 1024
|
||||
and not issymbolic(data)
|
||||
and self._concrete
|
||||
)
|
||||
|
||||
if can_write_raw:
|
||||
logger.debug("Using fast write")
|
||||
offset = mp._get_offset(where)
|
||||
if isinstance(data, str):
|
||||
data = bytes(data.encode('utf-8'))
|
||||
mp._data[offset:offset + len(data)] = data
|
||||
self._publish('did_write_memory', where, data, 8 * len(data))
|
||||
data = bytes(data.encode("utf-8"))
|
||||
mp._data[offset : offset + len(data)] = data
|
||||
self._publish("did_write_memory", where, data, 8 * len(data))
|
||||
else:
|
||||
for i in range(len(data)):
|
||||
self.write_int(where + i, Operators.ORD(data[i]), 8, force)
|
||||
@@ -753,9 +781,9 @@ class Cpu(Eventful):
|
||||
"""
|
||||
|
||||
if max_length is not None:
|
||||
string = string[:max_length - 1]
|
||||
string = string[: max_length - 1]
|
||||
|
||||
self.write_bytes(where, string + '\x00', force)
|
||||
self.write_bytes(where, string + "\x00", force)
|
||||
|
||||
def read_string(self, where, max_length=None, force=False):
|
||||
"""
|
||||
@@ -852,13 +880,13 @@ class Cpu(Eventful):
|
||||
if pc in self._instruction_cache:
|
||||
return self._instruction_cache[pc]
|
||||
|
||||
text = b''
|
||||
text = b""
|
||||
|
||||
# Read Instruction from memory
|
||||
for address in range(pc, pc + self.max_instr_width):
|
||||
# This reads a byte from memory ignoring permissions
|
||||
# and concretize it if symbolic
|
||||
if not self.memory.access_ok(address, 'x'):
|
||||
if not self.memory.access_ok(address, "x"):
|
||||
break
|
||||
|
||||
c = self.memory[address]
|
||||
@@ -870,19 +898,18 @@ class Cpu(Eventful):
|
||||
vals = visitors.simplify_array_select(c)
|
||||
c = bytes([vals[0]])
|
||||
except visitors.ArraySelectSimplifier.ExpressionNotSimple:
|
||||
c = struct.pack('B', Z3Solver().get_value(self.memory.constraints, c))
|
||||
c = struct.pack("B", Z3Solver().get_value(self.memory.constraints, c))
|
||||
elif isinstance(c, Constant):
|
||||
c = bytes([c.value])
|
||||
else:
|
||||
logger.error('Concretize executable memory %r %r', c, text)
|
||||
raise ConcretizeMemory(self.memory,
|
||||
address=pc,
|
||||
size=8 * self.max_instr_width,
|
||||
policy='INSTRUCTION')
|
||||
logger.error("Concretize executable memory %r %r", c, text)
|
||||
raise ConcretizeMemory(
|
||||
self.memory, address=pc, size=8 * self.max_instr_width, policy="INSTRUCTION"
|
||||
)
|
||||
text += c
|
||||
|
||||
# Pad potentially incomplete instruction with zeroes
|
||||
code = text.ljust(self.max_instr_width, b'\x00')
|
||||
code = text.ljust(self.max_instr_width, b"\x00")
|
||||
|
||||
try:
|
||||
# decode the instruction from code
|
||||
@@ -891,9 +918,9 @@ class Cpu(Eventful):
|
||||
raise DecodeException(pc, code)
|
||||
|
||||
# Check that the decoded instruction is contained in executable memory
|
||||
if not self.memory.access_ok(slice(pc, pc + insn.size), 'x'):
|
||||
if not self.memory.access_ok(slice(pc, pc + insn.size), "x"):
|
||||
logger.info("Trying to execute instructions from non-executable memory")
|
||||
raise InvalidMemoryAccess(pc, 'x')
|
||||
raise InvalidMemoryAccess(pc, "x")
|
||||
|
||||
insn.operands = self._wrap_operands(insn.operands)
|
||||
self._instruction_cache[pc] = insn
|
||||
@@ -919,16 +946,16 @@ class Cpu(Eventful):
|
||||
Decode, and execute one instruction pointed by register PC
|
||||
"""
|
||||
if issymbolic(self.PC):
|
||||
raise ConcretizeRegister(self, 'PC', policy='ALL')
|
||||
if not self.memory.access_ok(self.PC, 'x'):
|
||||
raise InvalidMemoryAccess(self.PC, 'x')
|
||||
raise ConcretizeRegister(self, "PC", policy="ALL")
|
||||
if not self.memory.access_ok(self.PC, "x"):
|
||||
raise InvalidMemoryAccess(self.PC, "x")
|
||||
|
||||
self._publish('will_decode_instruction', self.PC)
|
||||
self._publish("will_decode_instruction", self.PC)
|
||||
|
||||
insn = self.decode_instruction(self.PC)
|
||||
self._last_pc = self.PC
|
||||
|
||||
self._publish('will_execute_instruction', self.PC, insn)
|
||||
self._publish("will_execute_instruction", self.PC, insn)
|
||||
|
||||
# FIXME (theo) why just return here?
|
||||
if insn.address != self.PC:
|
||||
@@ -942,9 +969,9 @@ class Cpu(Eventful):
|
||||
register_logger.debug(l)
|
||||
|
||||
try:
|
||||
if self._concrete and 'SYSCALL' in name:
|
||||
if self._concrete and "SYSCALL" in name:
|
||||
self.emu.sync_unicorn_to_manticore()
|
||||
if self._concrete and 'SYSCALL' not in name:
|
||||
if self._concrete and "SYSCALL" not in name:
|
||||
self.emulate(insn)
|
||||
if self.PC == self._break_unicorn_at:
|
||||
logger.debug("Switching from Unicorn to Manticore")
|
||||
@@ -957,9 +984,14 @@ class Cpu(Eventful):
|
||||
implementation(*insn.operands)
|
||||
|
||||
else:
|
||||
text_bytes = ' '.join('%02x' % x for x in insn.bytes)
|
||||
logger.warning("Unimplemented instruction: 0x%016x:\t%s\t%s\t%s",
|
||||
insn.address, text_bytes, insn.mnemonic, insn.op_str)
|
||||
text_bytes = " ".join("%02x" % x for x in insn.bytes)
|
||||
logger.warning(
|
||||
"Unimplemented instruction: 0x%016x:\t%s\t%s\t%s",
|
||||
insn.address,
|
||||
text_bytes,
|
||||
insn.mnemonic,
|
||||
insn.op_str,
|
||||
)
|
||||
self.backup_emulate(insn)
|
||||
except (Interruption, Syscall) as e:
|
||||
e.on_handled = lambda: self._publish_instruction_as_executed(insn)
|
||||
@@ -975,7 +1007,7 @@ class Cpu(Eventful):
|
||||
Notify listeners that an instruction has been executed.
|
||||
"""
|
||||
self._icount += 1
|
||||
self._publish('did_execute_instruction', self._last_pc, self.PC, insn)
|
||||
self._publish("did_execute_instruction", self._last_pc, self.PC, insn)
|
||||
|
||||
def emulate(self, insn):
|
||||
"""
|
||||
@@ -1003,9 +1035,14 @@ class Cpu(Eventful):
|
||||
self.emu.emulate(insn)
|
||||
except unicorn.UcError as e:
|
||||
if e.errno == unicorn.UC_ERR_INSN_INVALID:
|
||||
text_bytes = ' '.join('%02x' % x for x in insn.bytes)
|
||||
logger.error("Unimplemented instruction: 0x%016x:\t%s\t%s\t%s",
|
||||
insn.address, text_bytes, insn.mnemonic, insn.op_str)
|
||||
text_bytes = " ".join("%02x" % x for x in insn.bytes)
|
||||
logger.error(
|
||||
"Unimplemented instruction: 0x%016x:\t%s\t%s\t%s",
|
||||
insn.address,
|
||||
text_bytes,
|
||||
insn.mnemonic,
|
||||
insn.op_str,
|
||||
)
|
||||
raise InstructionEmulationError(str(e))
|
||||
|
||||
def backup_emulate(self, insn):
|
||||
@@ -1016,15 +1053,20 @@ class Cpu(Eventful):
|
||||
:param capstone.CsInsn instruction: The instruction object to emulate
|
||||
"""
|
||||
|
||||
if not hasattr(self, 'backup_emu'):
|
||||
if not hasattr(self, "backup_emu"):
|
||||
self.backup_emu = UnicornEmulator(self)
|
||||
try:
|
||||
self.backup_emu.emulate(insn)
|
||||
except unicorn.UcError as e:
|
||||
if e.errno == unicorn.UC_ERR_INSN_INVALID:
|
||||
text_bytes = ' '.join('%02x' % x for x in insn.bytes)
|
||||
logger.error("Unimplemented instruction: 0x%016x:\t%s\t%s\t%s",
|
||||
insn.address, text_bytes, insn.mnemonic, insn.op_str)
|
||||
text_bytes = " ".join("%02x" % x for x in insn.bytes)
|
||||
logger.error(
|
||||
"Unimplemented instruction: 0x%016x:\t%s\t%s\t%s",
|
||||
insn.address,
|
||||
text_bytes,
|
||||
insn.mnemonic,
|
||||
insn.op_str,
|
||||
)
|
||||
raise InstructionEmulationError(str(e))
|
||||
finally:
|
||||
# We have been seeing occasional Unicorn issues with it not clearing
|
||||
@@ -1069,10 +1111,11 @@ class Cpu(Eventful):
|
||||
:rtype: str
|
||||
:return: name and current value for all the registers.
|
||||
"""
|
||||
result = f'{self.render_instruction()}\n'
|
||||
result += '\n'.join(self.render_registers())
|
||||
result = f"{self.render_instruction()}\n"
|
||||
result += "\n".join(self.render_registers())
|
||||
return result
|
||||
|
||||
|
||||
# Instruction decorators
|
||||
|
||||
|
||||
@@ -1082,5 +1125,6 @@ def instruction(old_method):
|
||||
def new_method(cpu, *args, **kw_args):
|
||||
cpu.PC += cpu.instruction.size
|
||||
return old_method(cpu, *args, **kw_args)
|
||||
|
||||
new_method.old_method = old_method
|
||||
return new_method
|
||||
|
||||
+294
-167
@@ -14,9 +14,7 @@ from ...utils.helpers import issymbolic
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# map different instructions to a single impl here
|
||||
OP_NAME_MAP = {
|
||||
'MOVW': 'MOV'
|
||||
}
|
||||
OP_NAME_MAP = {"MOVW": "MOV"}
|
||||
|
||||
|
||||
def HighBit(n):
|
||||
@@ -38,8 +36,9 @@ def instruction(body):
|
||||
# Let's remember next time we get here we should not do this again
|
||||
cpu._at_symbolic_conditional = cpu.instruction.address
|
||||
i_size = cpu.instruction.size
|
||||
cpu.PC = Operators.ITEBV(cpu.address_bit_size, should_execute, cpu.PC - i_size,
|
||||
cpu.PC)
|
||||
cpu.PC = Operators.ITEBV(
|
||||
cpu.address_bit_size, should_execute, cpu.PC - i_size, cpu.PC
|
||||
)
|
||||
return
|
||||
|
||||
if should_execute:
|
||||
@@ -54,11 +53,11 @@ def instruction(body):
|
||||
|
||||
|
||||
_TYPE_MAP = {
|
||||
cs.arm.ARM_OP_REG: 'register',
|
||||
cs.arm.ARM_OP_MEM: 'memory',
|
||||
cs.arm.ARM_OP_IMM: 'immediate',
|
||||
cs.arm.ARM_OP_PIMM: 'coprocessor',
|
||||
cs.arm.ARM_OP_CIMM: 'immediate'
|
||||
cs.arm.ARM_OP_REG: "register",
|
||||
cs.arm.ARM_OP_MEM: "memory",
|
||||
cs.arm.ARM_OP_IMM: "immediate",
|
||||
cs.arm.ARM_OP_PIMM: "coprocessor",
|
||||
cs.arm.ARM_OP_CIMM: "immediate",
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +75,7 @@ class Armv7Operand(Operand):
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
assert self.__type == 'register'
|
||||
assert self.__type == "register"
|
||||
if cs.arm.ARM_REG_D0 <= self.op.reg <= cs.arm.ARM_REG_D31:
|
||||
return 64
|
||||
else:
|
||||
@@ -84,12 +83,12 @@ class Armv7Operand(Operand):
|
||||
return 32
|
||||
|
||||
def read(self, nbits=None, with_carry=False):
|
||||
carry = self.cpu.regfile.read('APSR_C')
|
||||
if self.__type == 'register':
|
||||
carry = self.cpu.regfile.read("APSR_C")
|
||||
if self.__type == "register":
|
||||
value = self.cpu.regfile.read(self.reg)
|
||||
# PC in this case has to be set to the instruction after next. PC at this point
|
||||
# is already pointing to next instruction; we bump it one more.
|
||||
if self.reg in ('PC', 'R15'):
|
||||
if self.reg in ("PC", "R15"):
|
||||
value += self.cpu.instruction.size
|
||||
if self.is_shifted():
|
||||
shift = self.op.shift
|
||||
@@ -99,17 +98,17 @@ class Armv7Operand(Operand):
|
||||
if with_carry:
|
||||
return value, carry
|
||||
return value
|
||||
elif self.__type == 'immediate':
|
||||
elif self.__type == "immediate":
|
||||
imm = self.op.imm
|
||||
if self.op.subtracted:
|
||||
imm = -imm
|
||||
if with_carry:
|
||||
return imm, self._get_expand_imm_carry(carry)
|
||||
return imm
|
||||
elif self.__type == 'coprocessor':
|
||||
elif self.__type == "coprocessor":
|
||||
imm = self.op.imm
|
||||
return imm
|
||||
elif self.__type == 'memory':
|
||||
elif self.__type == "memory":
|
||||
val = self.cpu.read_int(self.address(), nbits)
|
||||
if with_carry:
|
||||
return val, carry
|
||||
@@ -118,17 +117,17 @@ class Armv7Operand(Operand):
|
||||
raise NotImplementedError("readOperand unknown type", self.op.type)
|
||||
|
||||
def write(self, value, nbits=None):
|
||||
if self.__type == 'register':
|
||||
if self.__type == "register":
|
||||
self.cpu.regfile.write(self.reg, value)
|
||||
elif self.__type == 'memory':
|
||||
raise NotImplementedError('need to impl arm store mem')
|
||||
elif self.__type == "memory":
|
||||
raise NotImplementedError("need to impl arm store mem")
|
||||
else:
|
||||
raise NotImplementedError("writeOperand unknown type", self.op.type)
|
||||
|
||||
def writeback(self, value):
|
||||
if self.__type == 'register':
|
||||
if self.__type == "register":
|
||||
self.write(value)
|
||||
elif self.__type == 'memory':
|
||||
elif self.__type == "memory":
|
||||
self.cpu.regfile.write(self.mem.base, value)
|
||||
else:
|
||||
raise NotImplementedError("writeback Operand unknown type", self.op.type)
|
||||
@@ -149,17 +148,17 @@ class Armv7Operand(Operand):
|
||||
return self.op.shift.type != cs.arm.ARM_SFT_INVALID
|
||||
|
||||
def address(self):
|
||||
assert self.__type == 'memory'
|
||||
assert self.__type == "memory"
|
||||
addr = self.get_mem_base_addr() + self.get_mem_offset()
|
||||
return addr & Mask(self.cpu.address_bit_size)
|
||||
|
||||
def get_mem_offset(self):
|
||||
assert self.__type == 'memory'
|
||||
assert self.__type == "memory"
|
||||
|
||||
off = 0
|
||||
if self.mem.index is not None:
|
||||
idx = self.mem.scale * self.cpu.regfile.read(self.mem.index)
|
||||
carry = self.cpu.regfile.read('APSR_C')
|
||||
carry = self.cpu.regfile.read("APSR_C")
|
||||
if self.is_shifted():
|
||||
shift = self.op.shift
|
||||
idx, carry = self.cpu._shift(idx, shift.type, shift.value, carry)
|
||||
@@ -169,7 +168,7 @@ class Armv7Operand(Operand):
|
||||
return off
|
||||
|
||||
def get_mem_base_addr(self):
|
||||
assert self.__type == 'memory'
|
||||
assert self.__type == "memory"
|
||||
|
||||
base = self.cpu.regfile.read(self.mem.base)
|
||||
|
||||
@@ -180,7 +179,7 @@ class Armv7Operand(Operand):
|
||||
#
|
||||
# Regardless of mode, our implementation of read(PC) will return the address
|
||||
# of the instruction following the next instruction.
|
||||
if self.mem.base in ('PC', 'R15'):
|
||||
if self.mem.base in ("PC", "R15"):
|
||||
if self.cpu.mode == cs.CS_MODE_ARM:
|
||||
logger.debug(f"ARM mode PC relative addressing: PC + offset: 0x{base:x} + 0x{4:x}")
|
||||
return base + 4
|
||||
@@ -189,14 +188,16 @@ class Armv7Operand(Operand):
|
||||
# we need (PC & 0xFFFFFFFC) + 4
|
||||
# thus:
|
||||
new_base = (base - self.cpu.instruction.size) & 0xFFFFFFFC
|
||||
logger.debug(f"THUMB mode PC relative addressing: ALIGN(PC) + offset => 0x{new_base:x} + 0x{4:x}")
|
||||
logger.debug(
|
||||
f"THUMB mode PC relative addressing: ALIGN(PC) + offset => 0x{new_base:x} + 0x{4:x}"
|
||||
)
|
||||
return new_base + 4
|
||||
else:
|
||||
return base
|
||||
|
||||
def _get_expand_imm_carry(self, carryIn):
|
||||
"""Manually compute the carry bit produced by expanding an immediate operand (see ARMExpandImm_C)"""
|
||||
insn = struct.unpack('<I', self.cpu.instruction.bytes)[0]
|
||||
insn = struct.unpack("<I", self.cpu.instruction.bytes)[0]
|
||||
unrotated = insn & Mask(8)
|
||||
shift = Operators.EXTRACT(insn, 8, 4)
|
||||
_, carry = self.cpu._shift(unrotated, cs.arm.ARM_SFT_ROR, 2 * shift, carryIn)
|
||||
@@ -209,53 +210,105 @@ class Armv7RegisterFile(RegisterFile):
|
||||
ARM Register file abstraction. GPRs use ints for read/write. APSR
|
||||
flags allow writes of bool/{1, 0} but always read bools.
|
||||
"""
|
||||
super().__init__({'SB': 'R9',
|
||||
'SL': 'R10',
|
||||
'FP': 'R11',
|
||||
'IP': 'R12',
|
||||
'STACK': 'R13',
|
||||
'SP': 'R13',
|
||||
'LR': 'R14',
|
||||
'PC': 'R15'})
|
||||
super().__init__(
|
||||
{
|
||||
"SB": "R9",
|
||||
"SL": "R10",
|
||||
"FP": "R11",
|
||||
"IP": "R12",
|
||||
"STACK": "R13",
|
||||
"SP": "R13",
|
||||
"LR": "R14",
|
||||
"PC": "R15",
|
||||
}
|
||||
)
|
||||
self._regs = {}
|
||||
# 32 bit registers
|
||||
for reg_name in ('R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8',
|
||||
'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15'):
|
||||
for reg_name in (
|
||||
"R0",
|
||||
"R1",
|
||||
"R2",
|
||||
"R3",
|
||||
"R4",
|
||||
"R5",
|
||||
"R6",
|
||||
"R7",
|
||||
"R8",
|
||||
"R9",
|
||||
"R10",
|
||||
"R11",
|
||||
"R12",
|
||||
"R13",
|
||||
"R14",
|
||||
"R15",
|
||||
):
|
||||
self._regs[reg_name] = Register(32)
|
||||
# 64 bit registers
|
||||
for reg_name in ('D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8',
|
||||
'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16',
|
||||
'D17', 'D18', 'D19', 'D20', 'D21', 'D22', 'D23', 'D24',
|
||||
'D25', 'D26', 'D27', 'D28', 'D29', 'D30', 'D31'):
|
||||
for reg_name in (
|
||||
"D0",
|
||||
"D1",
|
||||
"D2",
|
||||
"D3",
|
||||
"D4",
|
||||
"D5",
|
||||
"D6",
|
||||
"D7",
|
||||
"D8",
|
||||
"D9",
|
||||
"D10",
|
||||
"D11",
|
||||
"D12",
|
||||
"D13",
|
||||
"D14",
|
||||
"D15",
|
||||
"D16",
|
||||
"D17",
|
||||
"D18",
|
||||
"D19",
|
||||
"D20",
|
||||
"D21",
|
||||
"D22",
|
||||
"D23",
|
||||
"D24",
|
||||
"D25",
|
||||
"D26",
|
||||
"D27",
|
||||
"D28",
|
||||
"D29",
|
||||
"D30",
|
||||
"D31",
|
||||
):
|
||||
self._regs[reg_name] = Register(64)
|
||||
# Flags
|
||||
self._regs['APSR_N'] = Register(1)
|
||||
self._regs['APSR_Z'] = Register(1)
|
||||
self._regs['APSR_C'] = Register(1)
|
||||
self._regs['APSR_V'] = Register(1)
|
||||
self._regs['APSR_GE'] = Register(4)
|
||||
self._regs["APSR_N"] = Register(1)
|
||||
self._regs["APSR_Z"] = Register(1)
|
||||
self._regs["APSR_C"] = Register(1)
|
||||
self._regs["APSR_V"] = Register(1)
|
||||
self._regs["APSR_GE"] = Register(4)
|
||||
|
||||
# MMU Coprocessor -- to support MCR/MRC for TLS
|
||||
self._regs['P15_C13'] = Register(32)
|
||||
self._regs["P15_C13"] = Register(32)
|
||||
|
||||
def _read_APSR(self):
|
||||
def make_apsr_flag(flag_expr, offset):
|
||||
"""Helper for constructing an expression for the APSR register"""
|
||||
return Operators.ITEBV(32, flag_expr,
|
||||
BitVecConstant(32, 1 << offset),
|
||||
BitVecConstant(32, 0))
|
||||
return Operators.ITEBV(
|
||||
32, flag_expr, BitVecConstant(32, 1 << offset), BitVecConstant(32, 0)
|
||||
)
|
||||
|
||||
apsr = 0
|
||||
N = self.read('APSR_N')
|
||||
Z = self.read('APSR_Z')
|
||||
C = self.read('APSR_C')
|
||||
V = self.read('APSR_V')
|
||||
N = self.read("APSR_N")
|
||||
Z = self.read("APSR_Z")
|
||||
C = self.read("APSR_C")
|
||||
V = self.read("APSR_V")
|
||||
|
||||
if any(issymbolic(x) for x in [N, Z, C, V]):
|
||||
apsr = (make_apsr_flag(N, 31) |
|
||||
make_apsr_flag(Z, 30) |
|
||||
make_apsr_flag(C, 29) |
|
||||
make_apsr_flag(V, 28))
|
||||
apsr = (
|
||||
make_apsr_flag(N, 31)
|
||||
| make_apsr_flag(Z, 30)
|
||||
| make_apsr_flag(C, 29)
|
||||
| make_apsr_flag(V, 28)
|
||||
)
|
||||
else:
|
||||
if N:
|
||||
apsr |= 1 << 31
|
||||
@@ -274,39 +327,106 @@ class Armv7RegisterFile(RegisterFile):
|
||||
Z = Operators.EXTRACT(apsr, 30, 1)
|
||||
N = Operators.EXTRACT(apsr, 31, 1)
|
||||
|
||||
self.write('APSR_V', V)
|
||||
self.write('APSR_C', C)
|
||||
self.write('APSR_Z', Z)
|
||||
self.write('APSR_N', N)
|
||||
self.write("APSR_V", V)
|
||||
self.write("APSR_C", C)
|
||||
self.write("APSR_Z", Z)
|
||||
self.write("APSR_N", N)
|
||||
|
||||
def read(self, register):
|
||||
assert register in self
|
||||
if register == 'APSR':
|
||||
if register == "APSR":
|
||||
return self._read_APSR()
|
||||
register = self._alias(register)
|
||||
return self._regs[register].read()
|
||||
|
||||
def write(self, register, value):
|
||||
assert register in self
|
||||
if register == 'APSR':
|
||||
if register == "APSR":
|
||||
return self._write_APSR(value)
|
||||
register = self._alias(register)
|
||||
self._regs[register].write(value)
|
||||
|
||||
@property
|
||||
def all_registers(self):
|
||||
return super().all_registers + \
|
||||
('R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15',
|
||||
'D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15',
|
||||
'D16', 'D17', 'D18', 'D19', 'D20', 'D21', 'D22', 'D23', 'D24', 'D25', 'D26', 'D27', 'D28', 'D29',
|
||||
'D30', 'D31', 'APSR', 'APSR_N',
|
||||
'APSR_Z', 'APSR_C', 'APSR_V', 'APSR_GE',
|
||||
'P15_C13')
|
||||
return super().all_registers + (
|
||||
"R0",
|
||||
"R1",
|
||||
"R2",
|
||||
"R3",
|
||||
"R4",
|
||||
"R5",
|
||||
"R6",
|
||||
"R7",
|
||||
"R8",
|
||||
"R9",
|
||||
"R10",
|
||||
"R11",
|
||||
"R12",
|
||||
"R13",
|
||||
"R14",
|
||||
"R15",
|
||||
"D0",
|
||||
"D1",
|
||||
"D2",
|
||||
"D3",
|
||||
"D4",
|
||||
"D5",
|
||||
"D6",
|
||||
"D7",
|
||||
"D8",
|
||||
"D9",
|
||||
"D10",
|
||||
"D11",
|
||||
"D12",
|
||||
"D13",
|
||||
"D14",
|
||||
"D15",
|
||||
"D16",
|
||||
"D17",
|
||||
"D18",
|
||||
"D19",
|
||||
"D20",
|
||||
"D21",
|
||||
"D22",
|
||||
"D23",
|
||||
"D24",
|
||||
"D25",
|
||||
"D26",
|
||||
"D27",
|
||||
"D28",
|
||||
"D29",
|
||||
"D30",
|
||||
"D31",
|
||||
"APSR",
|
||||
"APSR_N",
|
||||
"APSR_Z",
|
||||
"APSR_C",
|
||||
"APSR_V",
|
||||
"APSR_GE",
|
||||
"P15_C13",
|
||||
)
|
||||
|
||||
@property
|
||||
def canonical_registers(self):
|
||||
return ('R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15',
|
||||
'APSR')
|
||||
return (
|
||||
"R0",
|
||||
"R1",
|
||||
"R2",
|
||||
"R3",
|
||||
"R4",
|
||||
"R5",
|
||||
"R6",
|
||||
"R7",
|
||||
"R8",
|
||||
"R9",
|
||||
"R10",
|
||||
"R11",
|
||||
"R12",
|
||||
"R13",
|
||||
"R14",
|
||||
"R15",
|
||||
"APSR",
|
||||
)
|
||||
|
||||
|
||||
class Armv7LinuxSyscallAbi(SyscallAbi):
|
||||
@@ -321,7 +441,7 @@ class Armv7LinuxSyscallAbi(SyscallAbi):
|
||||
|
||||
def get_arguments(self):
|
||||
for i in range(6):
|
||||
yield f'R{i}'
|
||||
yield f"R{i}"
|
||||
|
||||
def write_result(self, result):
|
||||
self._cpu.R0 = result
|
||||
@@ -332,7 +452,7 @@ class Armv7CdeclAbi(Abi):
|
||||
|
||||
def get_arguments(self):
|
||||
# First four passed via R0-R3, then on stack
|
||||
for reg in ('R0', 'R1', 'R2', 'R3'):
|
||||
for reg in ("R0", "R1", "R2", "R3"):
|
||||
yield reg
|
||||
|
||||
for address in self.values_from(self._cpu.STACK):
|
||||
@@ -353,33 +473,34 @@ class Armv7Cpu(Cpu):
|
||||
instruction + 4. However, official spec defines PC to be address of
|
||||
current instruction + 8 (section A2.3).
|
||||
"""
|
||||
|
||||
address_bit_size = 32
|
||||
max_instr_width = 4
|
||||
machine = 'armv7'
|
||||
machine = "armv7"
|
||||
arch = cs.CS_ARCH_ARM
|
||||
# 'mode' is usually defined here as a class member, but it can change, so
|
||||
# it's an instance property.
|
||||
|
||||
def __init__(self, memory):
|
||||
self._it_conditional = list()
|
||||
self._last_flags = {'C': 0, 'V': 0, 'N': 0, 'Z': 0, 'GE': 0}
|
||||
self._last_flags = {"C": 0, "V": 0, "N": 0, "Z": 0, "GE": 0}
|
||||
self._at_symbolic_conditional = None
|
||||
self._mode = cs.CS_MODE_ARM
|
||||
super().__init__(Armv7RegisterFile(), memory)
|
||||
|
||||
def __getstate__(self):
|
||||
state = super().__getstate__()
|
||||
state['_last_flags'] = self._last_flags
|
||||
state['at_symbolic_conditional'] = self._at_symbolic_conditional
|
||||
state['_it_conditional'] = self._it_conditional
|
||||
state['_mode'] = self._mode
|
||||
state["_last_flags"] = self._last_flags
|
||||
state["at_symbolic_conditional"] = self._at_symbolic_conditional
|
||||
state["_it_conditional"] = self._it_conditional
|
||||
state["_mode"] = self._mode
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
self._last_flags = state['_last_flags']
|
||||
self._at_symbolic_conditional = state['at_symbolic_conditional']
|
||||
self._it_conditional = state['_it_conditional']
|
||||
self._mode = state['_mode']
|
||||
self._last_flags = state["_last_flags"]
|
||||
self._at_symbolic_conditional = state["at_symbolic_conditional"]
|
||||
self._it_conditional = state["_it_conditional"]
|
||||
self._mode = state["_mode"]
|
||||
super().__setstate__(state)
|
||||
|
||||
@property
|
||||
@@ -397,14 +518,19 @@ class Armv7Cpu(Cpu):
|
||||
self.disasm.disasm.mode = new_mode
|
||||
|
||||
def _set_mode_by_val(self, val):
|
||||
new_mode = Operators.ITEBV(self.address_bit_size, (val & 0x1) == 0x1, cs.CS_MODE_THUMB, cs.CS_MODE_ARM)
|
||||
new_mode = Operators.ITEBV(
|
||||
self.address_bit_size, (val & 0x1) == 0x1, cs.CS_MODE_THUMB, cs.CS_MODE_ARM
|
||||
)
|
||||
|
||||
if issymbolic(new_mode):
|
||||
from ..state import Concretize
|
||||
|
||||
def set_concrete_mode(state, value):
|
||||
state.cpu.mode = value
|
||||
raise Concretize("Concretizing ARMv7 mode", expression=new_mode, setstate=set_concrete_mode)
|
||||
|
||||
raise Concretize(
|
||||
"Concretizing ARMv7 mode", expression=new_mode, setstate=set_concrete_mode
|
||||
)
|
||||
|
||||
self.mode = new_mode
|
||||
|
||||
@@ -433,21 +559,21 @@ class Armv7Cpu(Cpu):
|
||||
"""
|
||||
unupdated_flags = self._last_flags.keys() - flags.keys()
|
||||
for flag in unupdated_flags:
|
||||
flag_name = f'APSR_{flag}'
|
||||
flag_name = f"APSR_{flag}"
|
||||
self._last_flags[flag] = self.regfile.read(flag_name)
|
||||
self._last_flags.update(flags)
|
||||
|
||||
def commit_flags(self):
|
||||
# XXX: capstone incorrectly sets .update_flags for adc
|
||||
if self.instruction.mnemonic == 'adc':
|
||||
if self.instruction.mnemonic == "adc":
|
||||
return
|
||||
for flag, val in self._last_flags.items():
|
||||
flag_name = f'APSR_{flag}'
|
||||
flag_name = f"APSR_{flag}"
|
||||
self.regfile.write(flag_name, val)
|
||||
|
||||
def _shift(cpu, value, _type, amount, carry):
|
||||
"""See Shift() and Shift_C() in the ARM manual"""
|
||||
assert(cs.arm.ARM_SFT_INVALID < _type <= cs.arm.ARM_SFT_RRX_REG)
|
||||
assert cs.arm.ARM_SFT_INVALID < _type <= cs.arm.ARM_SFT_RRX_REG
|
||||
|
||||
# XXX: Capstone should set the value of an RRX shift to 1, which is
|
||||
# asserted in the manual, but it sets it to 0, so we have to check
|
||||
@@ -493,7 +619,7 @@ class Armv7Cpu(Cpu):
|
||||
self.SP -= len(data)
|
||||
self.write(self.SP, data)
|
||||
else:
|
||||
raise NotImplementedError('unsupported type for stack push data')
|
||||
raise NotImplementedError("unsupported type for stack push data")
|
||||
return self.SP
|
||||
|
||||
def stack_peek(self, nbytes=4):
|
||||
@@ -516,19 +642,19 @@ class Armv7Cpu(Cpu):
|
||||
return self.write_bytes(addr, data)
|
||||
|
||||
def set_arm_tls(self, data):
|
||||
self.regfile.write('P15_C13', data)
|
||||
self.regfile.write("P15_C13", data)
|
||||
|
||||
@staticmethod
|
||||
def canonicalize_instruction_name(instr):
|
||||
name = instr.insn_name().upper()
|
||||
# XXX bypass a capstone bug that incorrectly labels some insns as mov
|
||||
if name == 'MOV':
|
||||
if instr.mnemonic.startswith('lsr'):
|
||||
return 'LSR'
|
||||
elif instr.mnemonic.startswith('lsl'):
|
||||
return 'LSL'
|
||||
elif instr.mnemonic.startswith('asr'):
|
||||
return 'ASR'
|
||||
if name == "MOV":
|
||||
if instr.mnemonic.startswith("lsr"):
|
||||
return "LSR"
|
||||
elif instr.mnemonic.startswith("lsl"):
|
||||
return "LSL"
|
||||
elif instr.mnemonic.startswith("asr"):
|
||||
return "ASR"
|
||||
return OP_NAME_MAP.get(name, name)
|
||||
|
||||
def _wrap_operands(self, ops):
|
||||
@@ -556,10 +682,10 @@ class Armv7Cpu(Cpu):
|
||||
return cpu._evaluate_conditional(cc)
|
||||
|
||||
def _evaluate_conditional(cpu, cc):
|
||||
C = cpu.regfile.read('APSR_C')
|
||||
N = cpu.regfile.read('APSR_N')
|
||||
V = cpu.regfile.read('APSR_V')
|
||||
Z = cpu.regfile.read('APSR_Z')
|
||||
C = cpu.regfile.read("APSR_C")
|
||||
N = cpu.regfile.read("APSR_N")
|
||||
V = cpu.regfile.read("APSR_V")
|
||||
Z = cpu.regfile.read("APSR_Z")
|
||||
|
||||
if cc == cs.arm.ARM_CC_AL:
|
||||
ret = True
|
||||
@@ -603,9 +729,9 @@ class Armv7Cpu(Cpu):
|
||||
# this is incredibly hacky--how else does capstone expose this?
|
||||
# TODO: find a better way than string parsing the mnemonic -GR, 2017-07-13
|
||||
for c in cpu.instruction.mnemonic[1:]:
|
||||
if c == 't':
|
||||
if c == "t":
|
||||
cpu._it_conditional.append(true_case)
|
||||
elif c == 'e':
|
||||
elif c == "e":
|
||||
cpu._it_conditional.append(not true_case)
|
||||
|
||||
@instruction
|
||||
@@ -629,11 +755,14 @@ class Armv7Cpu(Cpu):
|
||||
op1val = op1.read()
|
||||
op2val = op2.read()
|
||||
result = list()
|
||||
GE = cpu.regfile.read('APSR_GE')
|
||||
GE = cpu.regfile.read("APSR_GE")
|
||||
for i in range(4):
|
||||
bit = Operators.EXTRACT(GE, i, 1)
|
||||
result.append(
|
||||
Operators.ITEBV(8, bit, Operators.EXTRACT(op1val, i * 8, 8), Operators.EXTRACT(op2val, i * 8, 8)))
|
||||
Operators.ITEBV(
|
||||
8, bit, Operators.EXTRACT(op1val, i * 8, 8), Operators.EXTRACT(op2val, i * 8, 8)
|
||||
)
|
||||
)
|
||||
dest.write(Operators.CONCAT(32, *reversed(result)))
|
||||
|
||||
@instruction
|
||||
@@ -665,7 +794,7 @@ class Armv7Cpu(Cpu):
|
||||
:param Armv7Operand dest: The destination operand; register
|
||||
:param Armv7Operand src: The source operand; 16-bit immediate
|
||||
"""
|
||||
assert src.type == 'immediate'
|
||||
assert src.type == "immediate"
|
||||
imm = src.read()
|
||||
low_halfword = dest.read() & Mask(16)
|
||||
dest.write((imm << 16) | low_halfword)
|
||||
@@ -682,10 +811,10 @@ class Armv7Cpu(Cpu):
|
||||
:param Armv7Operand coprocessor_reg_m: the coprocessor register; immediate
|
||||
:param Armv7Operand opcode2: coprocessor specific opcode; 3-bit immediate
|
||||
"""
|
||||
assert coprocessor.type == 'coprocessor'
|
||||
assert opcode1.type == 'immediate'
|
||||
assert opcode2.type == 'immediate'
|
||||
assert dest.type == 'register'
|
||||
assert coprocessor.type == "coprocessor"
|
||||
assert opcode1.type == "immediate"
|
||||
assert opcode2.type == "immediate"
|
||||
assert dest.type == "register"
|
||||
imm_coprocessor = coprocessor.read()
|
||||
imm_opcode1 = opcode1.read()
|
||||
imm_opcode2 = opcode2.read()
|
||||
@@ -696,16 +825,18 @@ class Armv7Cpu(Cpu):
|
||||
if 0 == imm_opcode1:
|
||||
if 13 == coprocessor_n_name:
|
||||
if 3 == imm_opcode2:
|
||||
dest.write(cpu.regfile.read('P15_C13'))
|
||||
dest.write(cpu.regfile.read("P15_C13"))
|
||||
return
|
||||
raise NotImplementedError("MRC: unimplemented combination of coprocessor, opcode, and coprocessor register")
|
||||
raise NotImplementedError(
|
||||
"MRC: unimplemented combination of coprocessor, opcode, and coprocessor register"
|
||||
)
|
||||
|
||||
@instruction
|
||||
def LDRD(cpu, dest1, dest2, src, offset=None):
|
||||
"""Loads double width data from memory."""
|
||||
assert dest1.type == 'register'
|
||||
assert dest2.type == 'register'
|
||||
assert src.type == 'memory'
|
||||
assert dest1.type == "register"
|
||||
assert dest2.type == "register"
|
||||
assert src.type == "memory"
|
||||
mem1 = cpu.read_int(src.address(), 32)
|
||||
mem2 = cpu.read_int(src.address() + 4, 32)
|
||||
writeback = cpu._compute_writeback(src, offset)
|
||||
@@ -716,9 +847,9 @@ class Armv7Cpu(Cpu):
|
||||
@instruction
|
||||
def STRD(cpu, src1, src2, dest, offset=None):
|
||||
"""Writes the contents of two registers to memory."""
|
||||
assert src1.type == 'register'
|
||||
assert src2.type == 'register'
|
||||
assert dest.type == 'memory'
|
||||
assert src1.type == "register"
|
||||
assert src2.type == "register"
|
||||
assert dest.type == "memory"
|
||||
val1 = src1.read()
|
||||
val2 = src2.read()
|
||||
writeback = cpu._compute_writeback(dest, offset)
|
||||
@@ -829,7 +960,7 @@ class Armv7Cpu(Cpu):
|
||||
word = Operators.SEXTEND(mem, width, cpu.address_bit_size)
|
||||
else:
|
||||
word = Operators.ZEXTEND(mem, cpu.address_bit_size)
|
||||
if dest.reg in ('PC', 'R15'):
|
||||
if dest.reg in ("PC", "R15"):
|
||||
cpu._set_mode_by_val(word)
|
||||
word &= ~0x1
|
||||
logger.debug(f"LDR writing 0x{word:x} -> PC")
|
||||
@@ -876,16 +1007,13 @@ class Armv7Cpu(Cpu):
|
||||
carry_out = UInt(result, W * 2) != unsigned_sum
|
||||
overflow = SInt(Operators.SEXTEND(result, W, W * 2), W * 2) != signed_sum
|
||||
|
||||
cpu.set_flags(C=carry_out,
|
||||
V=overflow,
|
||||
N=HighBit(result),
|
||||
Z=result == 0)
|
||||
cpu.set_flags(C=carry_out, V=overflow, N=HighBit(result), Z=result == 0)
|
||||
|
||||
return result, carry_out, overflow
|
||||
|
||||
@instruction
|
||||
def ADC(cpu, dest, op1, op2=None):
|
||||
carry = cpu.regfile.read('APSR_C')
|
||||
carry = cpu.regfile.read("APSR_C")
|
||||
if op2 is not None:
|
||||
result, carry, overflow = cpu._ADD(op1.read(), op2.read(), carry)
|
||||
else:
|
||||
@@ -912,7 +1040,7 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@instruction
|
||||
def RSC(cpu, dest, src, add):
|
||||
carry = cpu.regfile.read('APSR_C')
|
||||
carry = cpu.regfile.read("APSR_C")
|
||||
inv_src = GetNBits(~src.read(), cpu.address_bit_size)
|
||||
result, carry, overflow = cpu._ADD(inv_src, add.read(), carry)
|
||||
dest.write(result)
|
||||
@@ -931,7 +1059,7 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@instruction
|
||||
def SBC(cpu, dest, op1, op2=None):
|
||||
carry = cpu.regfile.read('APSR_C')
|
||||
carry = cpu.regfile.read("APSR_C")
|
||||
if op2 is not None:
|
||||
result, carry, overflow = cpu._ADD(op1.read(), ~op2.read(), carry)
|
||||
else:
|
||||
@@ -950,7 +1078,7 @@ class Armv7Cpu(Cpu):
|
||||
<Rd>. The assembler calculates the required value of the offset from the Align(PC,4)
|
||||
value of the ADR instruction to this label.
|
||||
"""
|
||||
aligned_pc = (cpu.instruction.address + 4) & 0xfffffffc
|
||||
aligned_pc = (cpu.instruction.address + 4) & 0xFFFFFFFC
|
||||
dest.write(aligned_pc + src.read())
|
||||
|
||||
@instruction
|
||||
@@ -967,8 +1095,8 @@ class Armv7Cpu(Cpu):
|
||||
Specifies the immediate value to be added to the value obtained from src. The range of allowed values is
|
||||
0-4095.
|
||||
"""
|
||||
aligned_pc = (cpu.instruction.address + 4) & 0xfffffffc
|
||||
if src.type == 'register' and src.reg in ('PC', 'R15'):
|
||||
aligned_pc = (cpu.instruction.address + 4) & 0xFFFFFFFC
|
||||
if src.type == "register" and src.reg in ("PC", "R15"):
|
||||
src = aligned_pc
|
||||
else:
|
||||
src = src.read()
|
||||
@@ -988,8 +1116,8 @@ class Armv7Cpu(Cpu):
|
||||
Specifies the immediate value to be added to the value obtained from src. The range of allowed values is
|
||||
0-4095.
|
||||
"""
|
||||
aligned_pc = (cpu.instruction.address + 4) & 0xfffffffc
|
||||
if src.type == 'register' and src.reg in ('PC', 'R15'):
|
||||
aligned_pc = (cpu.instruction.address + 4) & 0xFFFFFFFC
|
||||
if src.type == "register" and src.reg in ("PC", "R15"):
|
||||
src = aligned_pc
|
||||
else:
|
||||
src = src.read()
|
||||
@@ -1007,10 +1135,9 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@instruction
|
||||
def BLE(cpu, dest):
|
||||
cpu.PC = Operators.ITEBV(cpu.address_bit_size,
|
||||
cpu.regfile.read('APSR_Z'),
|
||||
dest.read(),
|
||||
cpu.PC)
|
||||
cpu.PC = Operators.ITEBV(
|
||||
cpu.address_bit_size, cpu.regfile.read("APSR_Z"), dest.read(), cpu.PC
|
||||
)
|
||||
|
||||
@instruction
|
||||
def CBZ(cpu, op, dest):
|
||||
@@ -1025,8 +1152,7 @@ class Armv7Cpu(Cpu):
|
||||
selects an encoding that will set imm32 to that offset. Allowed offsets are even numbers in
|
||||
the range 0 to 126.
|
||||
"""
|
||||
cpu.PC = Operators.ITEBV(cpu.address_bit_size,
|
||||
op.read(), cpu.PC, dest.read())
|
||||
cpu.PC = Operators.ITEBV(cpu.address_bit_size, op.read(), cpu.PC, dest.read())
|
||||
|
||||
@instruction
|
||||
def CBNZ(cpu, op, dest):
|
||||
@@ -1041,35 +1167,34 @@ class Armv7Cpu(Cpu):
|
||||
selects an encoding that will set imm32 to that offset. Allowed offsets are even numbers in
|
||||
the range 0 to 126.
|
||||
"""
|
||||
cpu.PC = Operators.ITEBV(cpu.address_bit_size,
|
||||
op.read(), dest.read(), cpu.PC)
|
||||
cpu.PC = Operators.ITEBV(cpu.address_bit_size, op.read(), dest.read(), cpu.PC)
|
||||
|
||||
@instruction
|
||||
def BL(cpu, label):
|
||||
next_instr_addr = cpu.regfile.read('PC')
|
||||
next_instr_addr = cpu.regfile.read("PC")
|
||||
if cpu.mode == cs.CS_MODE_THUMB:
|
||||
cpu.regfile.write('LR', next_instr_addr + 1)
|
||||
cpu.regfile.write("LR", next_instr_addr + 1)
|
||||
else:
|
||||
cpu.regfile.write('LR', next_instr_addr)
|
||||
cpu.regfile.write('PC', label.read())
|
||||
cpu.regfile.write("LR", next_instr_addr)
|
||||
cpu.regfile.write("PC", label.read())
|
||||
|
||||
@instruction
|
||||
def BLX(cpu, dest):
|
||||
address = cpu.PC
|
||||
target = dest.read()
|
||||
next_instr_addr = cpu.regfile.read('PC')
|
||||
next_instr_addr = cpu.regfile.read("PC")
|
||||
if cpu.mode == cs.CS_MODE_THUMB:
|
||||
cpu.regfile.write('LR', next_instr_addr + 1)
|
||||
cpu.regfile.write("LR", next_instr_addr + 1)
|
||||
else:
|
||||
cpu.regfile.write('LR', next_instr_addr)
|
||||
cpu.regfile.write('PC', target & ~1)
|
||||
cpu.regfile.write("LR", next_instr_addr)
|
||||
cpu.regfile.write("PC", target & ~1)
|
||||
|
||||
# The `blx <label>` form of this instruction forces a mode swap
|
||||
# Otherwise check the lsb of the destination and set the mode
|
||||
if dest.type == 'immediate':
|
||||
if dest.type == "immediate":
|
||||
logger.debug(f"swapping mode due to BLX at inst 0x{address:x}")
|
||||
cpu._swap_mode()
|
||||
elif dest.type == 'register':
|
||||
elif dest.type == "register":
|
||||
cpu._set_mode_by_val(dest.read())
|
||||
|
||||
@instruction
|
||||
@@ -1086,7 +1211,7 @@ class Armv7Cpu(Cpu):
|
||||
# Specifies the base register. This contains the address of the table of branch lengths. This
|
||||
# register is allowed to be the PC. If it is, the table immediately follows this instruction.
|
||||
base_addr = dest.get_mem_base_addr()
|
||||
if dest.mem.base in ('PC', 'R15'):
|
||||
if dest.mem.base in ("PC", "R15"):
|
||||
base_addr = cpu.PC
|
||||
|
||||
# Specifies the index register. This contains an integer pointing to a single byte within the
|
||||
@@ -1094,7 +1219,7 @@ class Armv7Cpu(Cpu):
|
||||
offset = cpu.read_int(base_addr + dest.get_mem_offset(), 8)
|
||||
offset = Operators.ZEXTEND(offset, cpu.address_bit_size)
|
||||
|
||||
cpu.PC += (offset << 1)
|
||||
cpu.PC += offset << 1
|
||||
|
||||
@instruction
|
||||
def TBH(cpu, dest):
|
||||
@@ -1110,7 +1235,7 @@ class Armv7Cpu(Cpu):
|
||||
# Specifies the base register. This contains the address of the table of branch lengths. This
|
||||
# register is allowed to be the PC. If it is, the table immediately follows this instruction.
|
||||
base_addr = dest.get_mem_base_addr()
|
||||
if dest.mem.base in ('PC', 'R15'):
|
||||
if dest.mem.base in ("PC", "R15"):
|
||||
base_addr = cpu.PC
|
||||
|
||||
# Specifies the index register. This contains an integer pointing to a halfword within the table.
|
||||
@@ -1118,7 +1243,7 @@ class Armv7Cpu(Cpu):
|
||||
offset = cpu.read_int(base_addr + dest.get_mem_offset(), 16)
|
||||
offset = Operators.ZEXTEND(offset, cpu.address_bit_size)
|
||||
|
||||
cpu.PC += (offset << 1)
|
||||
cpu.PC += offset << 1
|
||||
|
||||
@instruction
|
||||
def CMP(cpu, reg, compare):
|
||||
@@ -1129,7 +1254,7 @@ class Armv7Cpu(Cpu):
|
||||
def POP(cpu, *regs):
|
||||
for reg in regs:
|
||||
val = cpu.stack_pop(cpu.address_bit_size // 8)
|
||||
if reg.reg in ('PC', 'R15'):
|
||||
if reg.reg in ("PC", "R15"):
|
||||
cpu._set_mode_by_val(val)
|
||||
val = val & ~0x1
|
||||
reg.write(val)
|
||||
@@ -1200,7 +1325,7 @@ class Armv7Cpu(Cpu):
|
||||
address += (1 if increment else -1) * (reg.size // 8)
|
||||
|
||||
reg.write(cpu.read_int(address, reg.size))
|
||||
if reg.reg in ('PC', 'R15'):
|
||||
if reg.reg in ("PC", "R15"):
|
||||
# The general-purpose registers loaded can include the PC. If they do, the word loaded for the PC is
|
||||
# treated as an address and a branch occurs to that address. In ARMv5 and above, bit[0] of the loaded
|
||||
# value determines whether execution continues after this branch in ARM state or in Thumb state, as
|
||||
@@ -1352,26 +1477,26 @@ class Armv7Cpu(Cpu):
|
||||
assert insn_id in (cs.arm.ARM_INS_ASR, cs.arm.ARM_INS_LSL, cs.arm.ARM_INS_LSR)
|
||||
|
||||
if insn_id == cs.arm.ARM_INS_ASR:
|
||||
if rest and rest[0].type == 'immediate':
|
||||
if rest and rest[0].type == "immediate":
|
||||
srtype = cs.arm.ARM_SFT_ASR
|
||||
else:
|
||||
srtype = cs.arm.ARM_SFT_ASR_REG
|
||||
elif insn_id == cs.arm.ARM_INS_LSL:
|
||||
if rest and rest[0].type == 'immediate':
|
||||
if rest and rest[0].type == "immediate":
|
||||
srtype = cs.arm.ARM_SFT_LSL
|
||||
else:
|
||||
srtype = cs.arm.ARM_SFT_LSL_REG
|
||||
elif insn_id == cs.arm.ARM_INS_LSR:
|
||||
if rest and rest[0].type == 'immediate':
|
||||
if rest and rest[0].type == "immediate":
|
||||
srtype = cs.arm.ARM_SFT_LSR
|
||||
else:
|
||||
srtype = cs.arm.ARM_SFT_LSR_REG
|
||||
|
||||
carry = cpu.regfile.read('APSR_C')
|
||||
if rest and rest[0].type == 'register':
|
||||
carry = cpu.regfile.read("APSR_C")
|
||||
if rest and rest[0].type == "register":
|
||||
# FIXME we should make Operand.op private (and not accessible)
|
||||
result, carry = cpu._shift(op.read(), srtype, rest[0].op.reg, carry)
|
||||
elif rest and rest[0].type == 'immediate':
|
||||
elif rest and rest[0].type == "immediate":
|
||||
amount = rest[0].read()
|
||||
result, carry = cpu._shift(op.read(), srtype, amount, carry)
|
||||
elif cpu.mode == cs.CS_MODE_THUMB:
|
||||
@@ -1396,7 +1521,9 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@instruction
|
||||
def UMULL(cpu, rdlo, rdhi, rn, rm):
|
||||
result = UInt(rn.read(), cpu.address_bit_size * 2) * UInt(rm.read(), cpu.address_bit_size * 2)
|
||||
result = UInt(rn.read(), cpu.address_bit_size * 2) * UInt(
|
||||
rm.read(), cpu.address_bit_size * 2
|
||||
)
|
||||
rdhi.write(Operators.EXTRACT(result, cpu.address_bit_size, cpu.address_bit_size))
|
||||
rdlo.write(GetNBits(result, cpu.address_bit_size))
|
||||
cpu.set_flags(N=Bit(result, 63), Z=(result == 0))
|
||||
|
||||
@@ -57,9 +57,12 @@ def SInt(value, width):
|
||||
:return: The converted value
|
||||
:rtype int or long or BitVec
|
||||
"""
|
||||
return Operators.ITEBV(width, Bit(value, width - 1) == 1,
|
||||
GetNBits(value, width) - 2**width,
|
||||
GetNBits(value, width))
|
||||
return Operators.ITEBV(
|
||||
width,
|
||||
Bit(value, width - 1) == 1,
|
||||
GetNBits(value, width) - 2 ** width,
|
||||
GetNBits(value, width),
|
||||
)
|
||||
|
||||
|
||||
def UInt(value, width):
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
from .aarch64 import Aarch64Cpu, Aarch64CdeclAbi, Aarch64LinuxSyscallAbi
|
||||
from .arm import Armv7Cpu, Armv7CdeclAbi, Armv7LinuxSyscallAbi
|
||||
from .x86 import AMD64Cpu, I386Cpu, AMD64LinuxSyscallAbi, I386LinuxSyscallAbi, I386CdeclAbi, SystemVAbi
|
||||
from .x86 import (
|
||||
AMD64Cpu,
|
||||
I386Cpu,
|
||||
AMD64LinuxSyscallAbi,
|
||||
I386LinuxSyscallAbi,
|
||||
I386CdeclAbi,
|
||||
SystemVAbi,
|
||||
)
|
||||
|
||||
|
||||
class CpuFactory:
|
||||
_cpus = {
|
||||
'i386': I386Cpu,
|
||||
'amd64': AMD64Cpu,
|
||||
'armv7': Armv7Cpu,
|
||||
'aarch64': Aarch64Cpu
|
||||
}
|
||||
_cpus = {"i386": I386Cpu, "amd64": AMD64Cpu, "armv7": Armv7Cpu, "aarch64": Aarch64Cpu}
|
||||
|
||||
_linux_abis = {
|
||||
'i386': I386CdeclAbi,
|
||||
'amd64': SystemVAbi,
|
||||
'armv7': Armv7CdeclAbi,
|
||||
'aarch64': Aarch64CdeclAbi,
|
||||
"i386": I386CdeclAbi,
|
||||
"amd64": SystemVAbi,
|
||||
"armv7": Armv7CdeclAbi,
|
||||
"aarch64": Aarch64CdeclAbi,
|
||||
}
|
||||
|
||||
_linux_syscalls_abis = {
|
||||
'i386': I386LinuxSyscallAbi,
|
||||
'amd64': AMD64LinuxSyscallAbi,
|
||||
'armv7': Armv7LinuxSyscallAbi,
|
||||
'aarch64': Aarch64LinuxSyscallAbi
|
||||
"i386": I386LinuxSyscallAbi,
|
||||
"amd64": AMD64LinuxSyscallAbi,
|
||||
"armv7": Armv7LinuxSyscallAbi,
|
||||
"aarch64": Aarch64LinuxSyscallAbi,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
@@ -33,14 +35,14 @@ class CpuFactory:
|
||||
|
||||
@staticmethod
|
||||
def get_function_abi(cpu, os, machine):
|
||||
if os != 'linux' or machine not in CpuFactory._linux_abis:
|
||||
if os != "linux" or machine not in CpuFactory._linux_abis:
|
||||
raise NotImplementedError(f"OS and machine combination not supported: {os}/{machine}")
|
||||
|
||||
return CpuFactory._linux_abis[machine](cpu)
|
||||
|
||||
@staticmethod
|
||||
def get_syscall_abi(cpu, os, machine):
|
||||
if os != 'linux' or machine not in CpuFactory._linux_syscalls_abis:
|
||||
if os != "linux" or machine not in CpuFactory._linux_syscalls_abis:
|
||||
raise NotImplementedError(f"OS and machine combination not supported: {os}/{machine}")
|
||||
|
||||
return CpuFactory._linux_syscalls_abis[machine](cpu)
|
||||
|
||||
@@ -6,6 +6,7 @@ import capstone as cs
|
||||
class Instruction:
|
||||
"""Capstone-like instruction to be used internally
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def address(self):
|
||||
|
||||
@@ -27,4 +27,4 @@ class Register:
|
||||
if self.is_flag():
|
||||
self.value = bool(self.value)
|
||||
else:
|
||||
raise TypeError(f'Cannot store {val.__class__.__name__} in Register')
|
||||
raise TypeError(f"Cannot store {val.__class__.__name__} in Register")
|
||||
|
||||
+1143
-808
File diff suppressed because it is too large
Load Diff
@@ -18,24 +18,23 @@ from ..utils.helpers import issymbolic
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
consts = config.get_group('native')
|
||||
consts.add('stdin_size', default=256, description='Maximum symbolic stdin size')
|
||||
consts = config.get_group("native")
|
||||
consts.add("stdin_size", default=256, description="Maximum symbolic stdin size")
|
||||
|
||||
|
||||
class Manticore(ManticoreBase):
|
||||
|
||||
def generate_testcase(self, state, message='test'):
|
||||
def generate_testcase(self, state, message="test"):
|
||||
testcase = super().generate_testcase(state, message)
|
||||
self._output.save_testcase(state, testcase, message)
|
||||
|
||||
def __init__(self, path_or_state, argv=None, workspace_url=None, policy='random', **kwargs):
|
||||
def __init__(self, path_or_state, argv=None, workspace_url=None, policy="random", **kwargs):
|
||||
"""
|
||||
:param path_or_state: Path to binary or a state (object) to begin from.
|
||||
:param argv: arguments passed to the binary.
|
||||
"""
|
||||
if isinstance(path_or_state, str):
|
||||
if not os.path.isfile(path_or_state):
|
||||
raise OSError(f'{path_or_state} is not an existing regular file')
|
||||
raise OSError(f"{path_or_state} is not an existing regular file")
|
||||
initial_state = _make_initial_state(path_or_state, argv=argv, **kwargs)
|
||||
else:
|
||||
initial_state = path_or_state
|
||||
@@ -48,7 +47,7 @@ class Manticore(ManticoreBase):
|
||||
# sugar for 'will_execute_instruction"
|
||||
self._hooks = {}
|
||||
|
||||
#self.subscribe('will_generate_testcase', self._generate_testcase_callback)
|
||||
# self.subscribe('will_generate_testcase', self._generate_testcase_callback)
|
||||
|
||||
############################################################################
|
||||
# Model hooks + callback
|
||||
@@ -62,20 +61,21 @@ class Manticore(ManticoreBase):
|
||||
import importlib
|
||||
from manticore import platforms
|
||||
|
||||
with open(path, 'r') as fnames:
|
||||
with open(path, "r") as fnames:
|
||||
for line in fnames.readlines():
|
||||
address, cc_name, name = line.strip().split(' ')
|
||||
address, cc_name, name = line.strip().split(" ")
|
||||
fmodel = platforms
|
||||
name_parts = name.split('.')
|
||||
importlib.import_module(f".platforms.{name_parts[0]}", 'manticore')
|
||||
name_parts = name.split(".")
|
||||
importlib.import_module(f".platforms.{name_parts[0]}", "manticore")
|
||||
for n in name_parts:
|
||||
fmodel = getattr(fmodel, n)
|
||||
assert fmodel != platforms
|
||||
|
||||
def cb_function(state):
|
||||
state.platform.invoke_model(fmodel, prefix_args=(state.platform,))
|
||||
|
||||
self._model_hooks.setdefault(int(address, 0), set()).add(cb_function)
|
||||
self._executor.subscribe('will_execute_instruction', self._model_hook_callback)
|
||||
self._executor.subscribe("will_execute_instruction", self._model_hook_callback)
|
||||
|
||||
def _model_hook_callback(self, state, instruction):
|
||||
pc = state.cpu.PC
|
||||
@@ -89,7 +89,18 @@ class Manticore(ManticoreBase):
|
||||
self._output.save_testcase(state, testcase, message)
|
||||
|
||||
@classmethod
|
||||
def linux(cls, path, argv=None, envp=None, entry_symbol=None, symbolic_files=None, concrete_start='', pure_symbolic=False, stdin_size=None, **kwargs):
|
||||
def linux(
|
||||
cls,
|
||||
path,
|
||||
argv=None,
|
||||
envp=None,
|
||||
entry_symbol=None,
|
||||
symbolic_files=None,
|
||||
concrete_start="",
|
||||
pure_symbolic=False,
|
||||
stdin_size=None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Constructor for Linux binary analysis.
|
||||
|
||||
@@ -112,12 +123,24 @@ class Manticore(ManticoreBase):
|
||||
stdin_size = consts.stdin_size
|
||||
|
||||
try:
|
||||
return cls(_make_linux(path, argv, envp, entry_symbol, symbolic_files, concrete_start, pure_symbolic, stdin_size), **kwargs)
|
||||
return cls(
|
||||
_make_linux(
|
||||
path,
|
||||
argv,
|
||||
envp,
|
||||
entry_symbol,
|
||||
symbolic_files,
|
||||
concrete_start,
|
||||
pure_symbolic,
|
||||
stdin_size,
|
||||
),
|
||||
**kwargs,
|
||||
)
|
||||
except elftools.common.exceptions.ELFError:
|
||||
raise Exception(f'Invalid binary: {path}')
|
||||
raise Exception(f"Invalid binary: {path}")
|
||||
|
||||
@classmethod
|
||||
def decree(cls, path, concrete_start='', **kwargs):
|
||||
def decree(cls, path, concrete_start="", **kwargs):
|
||||
"""
|
||||
Constructor for Decree binary analysis.
|
||||
|
||||
@@ -130,7 +153,7 @@ class Manticore(ManticoreBase):
|
||||
try:
|
||||
return cls(_make_decree(path, concrete_start), **kwargs)
|
||||
except KeyError: # FIXME(mark) magic parsing for DECREE should raise better error
|
||||
raise Exception(f'Invalid binary: {path}')
|
||||
raise Exception(f"Invalid binary: {path}")
|
||||
|
||||
@property
|
||||
def binary_path(self):
|
||||
@@ -146,13 +169,13 @@ class Manticore(ManticoreBase):
|
||||
############################################################################
|
||||
|
||||
def load_assertions(self, path):
|
||||
with open(path, 'r') as f:
|
||||
with open(path, "r") as f:
|
||||
for line in f.readlines():
|
||||
pc = int(line.split(' ')[0], 16)
|
||||
pc = int(line.split(" ")[0], 16)
|
||||
if pc in self._assertions:
|
||||
logger.debug("Repeated PC in assertions file %s", path)
|
||||
self._assertions[pc] = ' '.join(line.split(' ')[1:])
|
||||
self.subscribe('will_execute_instruction', self._assertions_callback)
|
||||
self._assertions[pc] = " ".join(line.split(" ")[1:])
|
||||
self.subscribe("will_execute_instruction", self._assertions_callback)
|
||||
|
||||
def _assertions_callback(self, state, pc, instruction):
|
||||
if pc not in self._assertions:
|
||||
@@ -167,8 +190,9 @@ class Manticore(ManticoreBase):
|
||||
assertion = parse(program, state.cpu.read_int, state.cpu.read_register)
|
||||
if not Z3Solver().can_be_true(state.constraints, assertion):
|
||||
logger.info(str(state.cpu))
|
||||
logger.info("Assertion %x -> {%s} does not hold. Aborting state.",
|
||||
state.cpu.pc, program)
|
||||
logger.info(
|
||||
"Assertion %x -> {%s} does not hold. Aborting state.", state.cpu.pc, program
|
||||
)
|
||||
raise TerminateState()
|
||||
|
||||
# Everything is good add it.
|
||||
@@ -183,7 +207,7 @@ class Manticore(ManticoreBase):
|
||||
A decorator used to register a hook function to run before analysis begins. Hook
|
||||
function takes one :class:`~manticore.core.state.State` argument.
|
||||
"""
|
||||
self.subscribe('will_run', f)
|
||||
self.subscribe("will_run", f)
|
||||
return f
|
||||
|
||||
def hook(self, pc):
|
||||
@@ -194,9 +218,11 @@ class Manticore(ManticoreBase):
|
||||
:param pc: Address of instruction to hook
|
||||
:type pc: int or None
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self.add_hook(pc, f)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def add_hook(self, pc, callback):
|
||||
@@ -214,10 +240,10 @@ class Manticore(ManticoreBase):
|
||||
else:
|
||||
self._hooks.setdefault(pc, set()).add(callback)
|
||||
if self._hooks:
|
||||
self.subscribe('will_execute_instruction', self._hook_callback)
|
||||
self.subscribe("will_execute_instruction", self._hook_callback)
|
||||
|
||||
def _hook_callback(self, state, pc, instruction):
|
||||
'Invoke all registered generic hooks'
|
||||
"Invoke all registered generic hooks"
|
||||
|
||||
# Ignore symbolic pc.
|
||||
# TODO(yan): Should we ask the solver if any of the hooks are possible,
|
||||
@@ -250,7 +276,7 @@ class Manticore(ManticoreBase):
|
||||
:type line: int or None
|
||||
"""
|
||||
|
||||
with open(self.binary_path, 'rb') as f:
|
||||
with open(self.binary_path, "rb") as f:
|
||||
|
||||
elffile = ELFFile(f)
|
||||
|
||||
@@ -265,13 +291,13 @@ class Manticore(ManticoreBase):
|
||||
continue
|
||||
|
||||
# return first indexed memory address for the symbol,
|
||||
return symbols[0].entry['st_value']
|
||||
return symbols[0].entry["st_value"]
|
||||
|
||||
raise ValueError(f"The {self.binary_path} ELFfile does not contain symbol {symbol}")
|
||||
|
||||
def run(self, timeout=None):
|
||||
with self.locked_context() as context:
|
||||
context['time_started'] = time.time()
|
||||
context["time_started"] = time.time()
|
||||
|
||||
super().run(timeout=timeout)
|
||||
|
||||
@@ -285,21 +311,21 @@ class Manticore(ManticoreBase):
|
||||
time_ended = time.time()
|
||||
|
||||
with self.locked_context() as context:
|
||||
time_elapsed = time_ended - context['time_started']
|
||||
time_elapsed = time_ended - context["time_started"]
|
||||
|
||||
logger.info('Total time: %s', time_elapsed)
|
||||
logger.info("Total time: %s", time_elapsed)
|
||||
|
||||
context['time_ended'] = time_ended
|
||||
context['time_elapsed'] = time_elapsed
|
||||
context["time_ended"] = time_ended
|
||||
context["time_elapsed"] = time_elapsed
|
||||
|
||||
|
||||
def _make_initial_state(binary_path, **kwargs):
|
||||
with open(binary_path, 'rb') as f:
|
||||
with open(binary_path, "rb") as f:
|
||||
magic = f.read(4)
|
||||
if magic == b'\x7fELF':
|
||||
if magic == b"\x7fELF":
|
||||
# Linux
|
||||
state = _make_linux(binary_path, **kwargs)
|
||||
elif magic == b'\x7fCGC':
|
||||
elif magic == b"\x7fCGC":
|
||||
# Decree
|
||||
state = _make_decree(binary_path, **kwargs)
|
||||
else:
|
||||
@@ -307,37 +333,46 @@ def _make_initial_state(binary_path, **kwargs):
|
||||
return state
|
||||
|
||||
|
||||
def _make_decree(program, concrete_start='', **kwargs):
|
||||
def _make_decree(program, concrete_start="", **kwargs):
|
||||
from ..platforms import decree
|
||||
|
||||
constraints = ConstraintSet()
|
||||
platform = decree.SDecree(constraints, program)
|
||||
initial_state = State(constraints, platform)
|
||||
logger.info('Loading program %s', program)
|
||||
logger.info("Loading program %s", program)
|
||||
|
||||
if concrete_start != '':
|
||||
logger.info(f'Starting with concrete input: {concrete_start}')
|
||||
if concrete_start != "":
|
||||
logger.info(f"Starting with concrete input: {concrete_start}")
|
||||
platform.input.transmit(concrete_start)
|
||||
platform.input.transmit(initial_state.symbolicate_buffer('+' * 14, label='RECEIVE'))
|
||||
platform.input.transmit(initial_state.symbolicate_buffer("+" * 14, label="RECEIVE"))
|
||||
return initial_state
|
||||
|
||||
|
||||
def _make_linux(program, argv=None, env=None, entry_symbol=None, symbolic_files=None, concrete_start='', pure_symbolic=False, stdin_size=None):
|
||||
def _make_linux(
|
||||
program,
|
||||
argv=None,
|
||||
env=None,
|
||||
entry_symbol=None,
|
||||
symbolic_files=None,
|
||||
concrete_start="",
|
||||
pure_symbolic=False,
|
||||
stdin_size=None,
|
||||
):
|
||||
from ..platforms import linux
|
||||
|
||||
env = {} if env is None else env
|
||||
argv = [] if argv is None else argv
|
||||
env = [f'{k}={v}' for k, v in env.items()]
|
||||
env = [f"{k}={v}" for k, v in env.items()]
|
||||
|
||||
if stdin_size is None:
|
||||
stdin_size = consts.stdin_size
|
||||
|
||||
logger.info('Loading program %s', program)
|
||||
logger.info("Loading program %s", program)
|
||||
|
||||
constraints = ConstraintSet()
|
||||
platform = linux.SLinux(program, argv=argv, envp=env,
|
||||
symbolic_files=symbolic_files,
|
||||
pure_symbolic=pure_symbolic)
|
||||
platform = linux.SLinux(
|
||||
program, argv=argv, envp=env, symbolic_files=symbolic_files, pure_symbolic=pure_symbolic
|
||||
)
|
||||
if entry_symbol is not None:
|
||||
entry_pc = platform._find_symbol(entry_symbol)
|
||||
if entry_pc is None:
|
||||
@@ -345,22 +380,22 @@ def _make_linux(program, argv=None, env=None, entry_symbol=None, symbolic_files=
|
||||
raise Exception("Symbol not found")
|
||||
else:
|
||||
logger.info("Found symbol '%s' (%x)", entry_symbol, entry_pc)
|
||||
#TODO: use argv as arguments for function
|
||||
# TODO: use argv as arguments for function
|
||||
platform.set_entry(entry_pc)
|
||||
|
||||
initial_state = State(constraints, platform)
|
||||
|
||||
if concrete_start != '':
|
||||
logger.info('Starting with concrete input: %s', concrete_start)
|
||||
if concrete_start != "":
|
||||
logger.info("Starting with concrete input: %s", concrete_start)
|
||||
|
||||
if pure_symbolic:
|
||||
logger.warning('[EXPERIMENTAL] Using purely symbolic memory.')
|
||||
logger.warning("[EXPERIMENTAL] Using purely symbolic memory.")
|
||||
|
||||
for i, arg in enumerate(argv):
|
||||
argv[i] = initial_state.symbolicate_buffer(arg, label=f'ARGV{i + 1}')
|
||||
argv[i] = initial_state.symbolicate_buffer(arg, label=f"ARGV{i + 1}")
|
||||
|
||||
for i, evar in enumerate(env):
|
||||
env[i] = initial_state.symbolicate_buffer(evar, label=f'ENV{i + 1}')
|
||||
env[i] = initial_state.symbolicate_buffer(evar, label=f"ENV{i + 1}")
|
||||
|
||||
# If any of the arguments or environment refer to symbolic values, re-
|
||||
# initialize the stack
|
||||
@@ -370,6 +405,5 @@ def _make_linux(program, argv=None, env=None, entry_symbol=None, symbolic_files=
|
||||
platform.input.write(concrete_start)
|
||||
|
||||
# set stdin input...
|
||||
platform.input.write(initial_state.symbolicate_buffer('+' * stdin_size,
|
||||
label='STDIN'))
|
||||
platform.input.write(initial_state.symbolicate_buffer("+" * stdin_size, label="STDIN"))
|
||||
return initial_state
|
||||
|
||||
@@ -27,8 +27,14 @@ libc = get_libc()
|
||||
# void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset)
|
||||
mmap_function = libc.mmap
|
||||
mmap_function.restype = ctypes.c_void_p
|
||||
mmap_function.argtype = [ctypes.c_void_p, ctypes.c_size_t,
|
||||
ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_size_t]
|
||||
mmap_function.argtype = [
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_size_t,
|
||||
ctypes.c_int,
|
||||
ctypes.c_int,
|
||||
ctypes.c_int,
|
||||
ctypes.c_size_t,
|
||||
]
|
||||
|
||||
|
||||
# int munmap(void* addr, size_t len)
|
||||
@@ -44,11 +50,11 @@ def mmap(fd, offset, size):
|
||||
# When trying to map the contents of a file into memory, the offset must be a multiple of the page size (see
|
||||
# `man mmap`). So we need to align it before passing it to mmap(). Doing so also increases the size of the memory
|
||||
# area needed, so we need to account for that difference.
|
||||
aligned_offset = offset & ~0xfff
|
||||
aligned_offset = offset & ~0xFFF
|
||||
size += offset - aligned_offset
|
||||
|
||||
if size & 0xfff != 0:
|
||||
size = (size & ~0xfff) + 0x1000
|
||||
if size & 0xFFF != 0:
|
||||
size = (size & ~0xFFF) + 0x1000
|
||||
assert size > 0
|
||||
|
||||
result = mmap_function(0, size, prot, flags, fd, aligned_offset)
|
||||
@@ -64,7 +70,7 @@ def munmap(address, size):
|
||||
# pointer can be recovered by aligning it on the page size. The size needs to be increased by that difference to
|
||||
# account for cases where the requested memory area was larger.
|
||||
address = ctypes.cast(address, ctypes.c_void_p).value
|
||||
aligned_address = address & ~0xfff
|
||||
aligned_address = address & ~0xFFF
|
||||
size += address - aligned_address
|
||||
assert size > 0
|
||||
aligned_address = ctypes.cast(aligned_address, ctypes.POINTER(ctypes.c_char))
|
||||
|
||||
+156
-104
@@ -1,6 +1,15 @@
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from weakref import WeakValueDictionary
|
||||
from ..core.smtlib import Operators, ConstraintSet, arithmetic_simplify, Z3Solver, TooManySolutions, BitVec, BitVecConstant, expression
|
||||
from ..core.smtlib import (
|
||||
Operators,
|
||||
ConstraintSet,
|
||||
arithmetic_simplify,
|
||||
Z3Solver,
|
||||
TooManySolutions,
|
||||
BitVec,
|
||||
BitVecConstant,
|
||||
expression,
|
||||
)
|
||||
from ..native.mappings import mmap, munmap
|
||||
from ..utils.helpers import issymbolic, interval_intersection
|
||||
|
||||
@@ -25,7 +34,7 @@ class MemoryException(Exception):
|
||||
self.address = address
|
||||
self.message = message
|
||||
if address is not None and not issymbolic(address):
|
||||
self.message += f' <{address:x}>'
|
||||
self.message += f" <{address:x}>"
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
@@ -36,7 +45,7 @@ class ConcretizeMemory(MemoryException):
|
||||
Raised when a symbolic memory cell needs to be concretized.
|
||||
"""
|
||||
|
||||
def __init__(self, mem, address, size, message=None, policy='MINMAX'):
|
||||
def __init__(self, mem, address, size, message=None, policy="MINMAX"):
|
||||
if message is None:
|
||||
self.message = f"Concretizing memory address {address} size {size}"
|
||||
else:
|
||||
@@ -48,21 +57,21 @@ class ConcretizeMemory(MemoryException):
|
||||
|
||||
|
||||
class InvalidMemoryAccess(MemoryException):
|
||||
_message = 'Invalid memory access'
|
||||
_message = "Invalid memory access"
|
||||
|
||||
def __init__(self, address, mode):
|
||||
assert mode in 'rwx'
|
||||
suffix = f' (mode:{mode})'
|
||||
assert mode in "rwx"
|
||||
suffix = f" (mode:{mode})"
|
||||
message = self._message + suffix
|
||||
super(InvalidMemoryAccess, self, ).__init__(message, address)
|
||||
super(InvalidMemoryAccess, self).__init__(message, address)
|
||||
self.mode = mode
|
||||
|
||||
|
||||
class InvalidSymbolicMemoryAccess(InvalidMemoryAccess):
|
||||
_message = 'Invalid symbolic memory access'
|
||||
_message = "Invalid symbolic memory access"
|
||||
|
||||
def __init__(self, address, mode, size, constraint):
|
||||
super(InvalidSymbolicMemoryAccess, self, ).__init__(address, mode)
|
||||
super(InvalidSymbolicMemoryAccess, self).__init__(address, mode)
|
||||
# the crashing constraint you need to assert
|
||||
self.constraint = constraint
|
||||
self.size = size
|
||||
@@ -104,8 +113,8 @@ class Map(object, metaclass=ABCMeta):
|
||||
:param size: the size of the map.
|
||||
:param perms: the access permissions of the map (rwx).
|
||||
"""
|
||||
assert isinstance(start, int) and start >= 0, 'Invalid start address'
|
||||
assert isinstance(size, int) and size > 0, 'Invalid end address'
|
||||
assert isinstance(start, int) and start >= 0, "Invalid start address"
|
||||
assert isinstance(size, int) and size > 0, "Invalid end address"
|
||||
|
||||
super().__init__()
|
||||
self._start = start
|
||||
@@ -123,7 +132,11 @@ class Map(object, metaclass=ABCMeta):
|
||||
|
||||
:param perms: the new permissions.
|
||||
"""
|
||||
assert isinstance(perms, str) and len(perms) <= 3 and perms.strip() in ['', 'r', 'w', 'x', 'rw', 'r x', 'rx', 'rwx', 'wx', ]
|
||||
assert (
|
||||
isinstance(perms, str)
|
||||
and len(perms) <= 3
|
||||
and perms.strip() in ["", "r", "w", "x", "rw", "r x", "rx", "rwx", "wx"]
|
||||
)
|
||||
self._perms = perms
|
||||
|
||||
# Property
|
||||
@@ -158,7 +171,7 @@ class Map(object, metaclass=ABCMeta):
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return f'<{self.__class__.__name__} 0x{self.start:016x}-0x{self.end:016x} {self.perms}>'
|
||||
return f"<{self.__class__.__name__} 0x{self.start:016x}-0x{self.end:016x} {self.perms}>"
|
||||
|
||||
def __iter__(self):
|
||||
"""
|
||||
@@ -167,10 +180,12 @@ class Map(object, metaclass=ABCMeta):
|
||||
return iter(range(self._start, self._end))
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.start == other.start and \
|
||||
self.end == other.end and \
|
||||
self.perms == other.perms and \
|
||||
self.name == other.name
|
||||
return (
|
||||
self.start == other.start
|
||||
and self.end == other.end
|
||||
and self.perms == other.perms
|
||||
and self.name == other.name
|
||||
)
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.start != other.start:
|
||||
@@ -188,12 +203,11 @@ class Map(object, metaclass=ABCMeta):
|
||||
def _in_range(self, index):
|
||||
""" Returns True if index is in range """
|
||||
if isinstance(index, slice):
|
||||
in_range = index.start < index.stop and \
|
||||
index.start >= self.start and \
|
||||
index.stop <= self.end
|
||||
in_range = (
|
||||
index.start < index.stop and index.start >= self.start and index.stop <= self.end
|
||||
)
|
||||
else:
|
||||
in_range = index >= self.start and \
|
||||
index <= self.end
|
||||
in_range = index >= self.start and index <= self.end
|
||||
return in_range
|
||||
|
||||
def _get_offset(self, index):
|
||||
@@ -206,7 +220,7 @@ class Map(object, metaclass=ABCMeta):
|
||||
self.end -> len(self)
|
||||
"""
|
||||
if not self._in_range(index):
|
||||
raise IndexError('Map index out of range')
|
||||
raise IndexError("Map index out of range")
|
||||
if isinstance(index, slice):
|
||||
index = slice(index.start - self.start, index.stop - self.start)
|
||||
else:
|
||||
@@ -256,12 +270,12 @@ class AnonMap(Map):
|
||||
super().__init__(start, size, perms, name, **kwargs)
|
||||
self._data = bytearray(size)
|
||||
if data_init is not None:
|
||||
assert len(data_init) <= size, 'More initial data than reserved memory'
|
||||
assert len(data_init) <= size, "More initial data than reserved memory"
|
||||
# check that the values this slice points to are ints
|
||||
if isinstance(data_init[0], int):
|
||||
self._data[0:len(data_init)] = data_init
|
||||
self._data[0 : len(data_init)] = data_init
|
||||
else:
|
||||
self._data[0:len(data_init)] = [ord(s) for s in data_init]
|
||||
self._data[0 : len(data_init)] = [ord(s) for s in data_init]
|
||||
|
||||
def __reduce__(self):
|
||||
return (self.__class__, (self.start, len(self), self.perms, self._data, self.name))
|
||||
@@ -273,17 +287,18 @@ class AnonMap(Map):
|
||||
return self, None
|
||||
|
||||
assert address > self.start and address < self.end
|
||||
head = AnonMap(self.start, address - self.start, self.perms, self[self.start:address])
|
||||
tail = AnonMap(address, self.end - address, self.perms, self[address:self.end])
|
||||
head = AnonMap(self.start, address - self.start, self.perms, self[self.start : address])
|
||||
tail = AnonMap(address, self.end - address, self.perms, self[address : self.end])
|
||||
return head, tail
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
assert not isinstance(index, slice) or \
|
||||
len(value) == index.stop - index.start
|
||||
assert not isinstance(index, slice) or len(value) == index.stop - index.start
|
||||
index = self._get_offset(index)
|
||||
|
||||
if issymbolic(value[0]) and isinstance(self._data, bytearray):
|
||||
self._data = [Operators.ORD(b) for b in self._data] # completely convert everything to a list if we need to store some symbolic data, just copying fpse
|
||||
self._data = [
|
||||
Operators.ORD(b) for b in self._data
|
||||
] # completely convert everything to a list if we need to store some symbolic data, just copying fpse
|
||||
|
||||
if isinstance(index, slice):
|
||||
if not isinstance(value[0], int):
|
||||
@@ -303,14 +318,26 @@ class ArrayMap(Map):
|
||||
def __init__(self, address, size, perms, index_bits, backing_array=None, name=None, **kwargs):
|
||||
super(ArrayMap, self).__init__(address, size, perms)
|
||||
if name is None:
|
||||
name = 'ArrayMap_{:x}'.format(address)
|
||||
name = "ArrayMap_{:x}".format(address)
|
||||
if backing_array is not None:
|
||||
self._array = backing_array
|
||||
else:
|
||||
self._array = expression.ArrayProxy(expression.ArrayVariable(index_bits, index_max=size, value_bits=8, name=name))
|
||||
self._array = expression.ArrayProxy(
|
||||
expression.ArrayVariable(index_bits, index_max=size, value_bits=8, name=name)
|
||||
)
|
||||
|
||||
def __reduce__(self):
|
||||
return self.__class__, (self.start, len(self), self._perms, self._array.index_bits, self._array, self._array.name)
|
||||
return (
|
||||
self.__class__,
|
||||
(
|
||||
self.start,
|
||||
len(self),
|
||||
self._perms,
|
||||
self._array.index_bits,
|
||||
self._array,
|
||||
self._array.name,
|
||||
),
|
||||
)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._array[key] = value
|
||||
@@ -328,10 +355,14 @@ class ArrayMap(Map):
|
||||
index_bits, value_bits = self._array.index_bits, self._array.value_bits
|
||||
|
||||
left_size, right_size = address - self.start, self.end - address
|
||||
left_name, right_name = ['{}_{:d}'.format(self._array.name, i) for i in range(2)]
|
||||
left_name, right_name = ["{}_{:d}".format(self._array.name, i) for i in range(2)]
|
||||
|
||||
head_arr = expression.ArrayProxy(expression.ArrayVariable(index_bits, left_size, value_bits, name=left_name))
|
||||
tail_arr = expression.ArrayProxy(expression.ArrayVariable(index_bits, right_size, value_bits, name=right_name))
|
||||
head_arr = expression.ArrayProxy(
|
||||
expression.ArrayVariable(index_bits, left_size, value_bits, name=left_name)
|
||||
)
|
||||
tail_arr = expression.ArrayProxy(
|
||||
expression.ArrayVariable(index_bits, right_size, value_bits, name=right_name)
|
||||
)
|
||||
|
||||
head = ArrayMap(self.start, left_size, self.perms, index_bits, head_arr, left_name)
|
||||
tail = ArrayMap(address, right_size, self.perms, index_bits, tail_arr, right_name)
|
||||
@@ -366,7 +397,7 @@ class FileMap(Map):
|
||||
assert offset >= 0
|
||||
self._filename = filename
|
||||
self._offset = offset
|
||||
with open(filename, 'r') as fileobject:
|
||||
with open(filename, "r") as fileobject:
|
||||
fileobject.seek(0, 2)
|
||||
file_size = fileobject.tell()
|
||||
self._mapped_size = min(size, file_size - offset)
|
||||
@@ -377,18 +408,20 @@ class FileMap(Map):
|
||||
self._overlay = dict()
|
||||
|
||||
def __reduce__(self):
|
||||
return (self.__class__, (self.start, len(self), self.perms, self._filename, self._offset, self._overlay))
|
||||
return (
|
||||
self.__class__,
|
||||
(self.start, len(self), self.perms, self._filename, self._offset, self._overlay),
|
||||
)
|
||||
|
||||
def __del__(self):
|
||||
if hasattr(self, '_data'):
|
||||
if hasattr(self, "_data"):
|
||||
munmap(self._data, self._mapped_size)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<{self.__class__.__name__} [{self._filename}+{self._offset:x}] 0x{self.start:016x}-0x{self.end:016x} {self.perms}>'
|
||||
return f"<{self.__class__.__name__} [{self._filename}+{self._offset:x}] 0x{self.start:016x}-0x{self.end:016x} {self.perms}>"
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
assert not isinstance(index, slice) or \
|
||||
len(value) == index.stop - index.start
|
||||
assert not isinstance(index, slice) or len(value) == index.stop - index.start
|
||||
index = self._get_offset(index)
|
||||
if isinstance(index, slice):
|
||||
for i in range(index.stop - index.start):
|
||||
@@ -402,7 +435,7 @@ class FileMap(Map):
|
||||
return _normalize(self._overlay[offset])
|
||||
else:
|
||||
if offset >= self._mapped_size:
|
||||
return b'\x00' # , 'Extra data must initially be zero'
|
||||
return b"\x00" # , 'Extra data must initially be zero'
|
||||
return _normalize(self._data[offset])
|
||||
|
||||
index = self._get_offset(index)
|
||||
@@ -487,7 +520,6 @@ class COWMap(Map):
|
||||
|
||||
|
||||
class StubCPU:
|
||||
|
||||
def _publish(self, *args, **kwargs):
|
||||
return None
|
||||
|
||||
@@ -593,7 +625,7 @@ class Memory(object, metaclass=ABCMeta):
|
||||
"""
|
||||
assert size & self.page_mask == 0
|
||||
if start is None:
|
||||
end = {32: 0xf8000000, 64: 0x0000800000000000}[self.memory_bit_size]
|
||||
end = {32: 0xF8000000, 64: 0x0000800000000000}[self.memory_bit_size]
|
||||
start = end - size
|
||||
else:
|
||||
if start > self.memory_size - size:
|
||||
@@ -610,7 +642,7 @@ class Memory(object, metaclass=ABCMeta):
|
||||
return p << self.page_bit_size
|
||||
counter += 1
|
||||
if counter >= self.memory_size // self.page_size:
|
||||
raise MemoryException('Not enough memory')
|
||||
raise MemoryException("Not enough memory")
|
||||
|
||||
return self._search(size, self.memory_size - size, counter)
|
||||
|
||||
@@ -634,14 +666,14 @@ class Memory(object, metaclass=ABCMeta):
|
||||
- 'Map already used' if the piece of memory starting in C{addr} and with length C{size} isn't free.
|
||||
"""
|
||||
# If addr is NULL, the system determines where to allocate the region.
|
||||
assert addr is None or isinstance(addr, int), 'Address shall be concrete'
|
||||
assert addr is None or isinstance(addr, int), "Address shall be concrete"
|
||||
assert size > 0
|
||||
|
||||
self.cpu._publish('will_map_memory', addr, size, perms, filename, offset)
|
||||
self.cpu._publish("will_map_memory", addr, size, perms, filename, offset)
|
||||
|
||||
# address is rounded down to the nearest multiple of the allocation granularity
|
||||
if addr is not None:
|
||||
assert addr < self.memory_size, 'Address too big'
|
||||
assert addr < self.memory_size, "Address too big"
|
||||
addr = self._floor(addr)
|
||||
|
||||
# size value is rounded up to the next page boundary
|
||||
@@ -652,7 +684,7 @@ class Memory(object, metaclass=ABCMeta):
|
||||
|
||||
# It should not be allocated
|
||||
for i in range(self._page(addr), self._page(addr + size)):
|
||||
assert i not in self._page2map, 'Map already used'
|
||||
assert i not in self._page2map, "Map already used"
|
||||
|
||||
# Create the map
|
||||
m = FileMap(addr, size, perms, filename, offset)
|
||||
@@ -660,8 +692,8 @@ class Memory(object, metaclass=ABCMeta):
|
||||
# Okay, ready to alloc
|
||||
self._add(m)
|
||||
|
||||
logger.debug('New file-memory map @%x size:%x', addr, size)
|
||||
self.cpu._publish('did_map_memory', addr, size, perms, filename, offset, addr)
|
||||
logger.debug("New file-memory map @%x size:%x", addr, size)
|
||||
self.cpu._publish("did_map_memory", addr, size, perms, filename, offset, addr)
|
||||
return addr
|
||||
|
||||
def mmap(self, addr, size, perms, data_init=None, name=None):
|
||||
@@ -683,13 +715,13 @@ class Memory(object, metaclass=ABCMeta):
|
||||
|
||||
"""
|
||||
# If addr is NULL, the system determines where to allocate the region.
|
||||
assert addr is None or isinstance(addr, int), 'Address shall be concrete'
|
||||
assert addr is None or isinstance(addr, int), "Address shall be concrete"
|
||||
|
||||
self.cpu._publish('will_map_memory', addr, size, perms, None, None)
|
||||
self.cpu._publish("will_map_memory", addr, size, perms, None, None)
|
||||
|
||||
# address is rounded down to the nearest multiple of the allocation granularity
|
||||
if addr is not None:
|
||||
assert addr < self.memory_size, 'Address too big'
|
||||
assert addr < self.memory_size, "Address too big"
|
||||
addr = self._floor(addr)
|
||||
|
||||
# size value is rounded up to the next page boundary
|
||||
@@ -700,7 +732,7 @@ class Memory(object, metaclass=ABCMeta):
|
||||
|
||||
# It should not be allocated
|
||||
for i in range(self._page(addr), self._page(addr + size)):
|
||||
assert i not in self._page2map, 'Map already used'
|
||||
assert i not in self._page2map, "Map already used"
|
||||
|
||||
# Create the anonymous map
|
||||
m = AnonMap(start=addr, size=size, perms=perms, data_init=data_init, name=name)
|
||||
@@ -708,9 +740,9 @@ class Memory(object, metaclass=ABCMeta):
|
||||
# Okay, ready to alloc
|
||||
self._add(m)
|
||||
|
||||
logger.debug('New memory map @%x size:%x', addr, size)
|
||||
logger.debug("New memory map @%x size:%x", addr, size)
|
||||
|
||||
self.cpu._publish('did_map_memory', addr, size, perms, None, None, addr)
|
||||
self.cpu._publish("did_map_memory", addr, size, perms, None, None, addr)
|
||||
return addr
|
||||
|
||||
def _add(self, m):
|
||||
@@ -756,7 +788,7 @@ class Memory(object, metaclass=ABCMeta):
|
||||
result = []
|
||||
for m in self.maps:
|
||||
if isinstance(m, AnonMap):
|
||||
result.append((m.start, m.end, m.perms, 0, ''))
|
||||
result.append((m.start, m.end, m.perms, 0, ""))
|
||||
elif isinstance(m, FileMap):
|
||||
result.append((m.start, m.end, m.perms, m._offset, m._filename))
|
||||
else:
|
||||
@@ -765,7 +797,12 @@ class Memory(object, metaclass=ABCMeta):
|
||||
return sorted(result)
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join([f'{start:016x}-{end:016x} {p:>4s} {offset:08x} {name or ""}' for start, end, p, offset, name in self.mappings()])
|
||||
return "\n".join(
|
||||
[
|
||||
f'{start:016x}-{end:016x} {p:>4s} {offset:08x} {name or ""}'
|
||||
for start, end, p, offset, name in self.mappings()
|
||||
]
|
||||
)
|
||||
|
||||
def _maps_in_range(self, start, end):
|
||||
"""
|
||||
@@ -794,7 +831,7 @@ class Memory(object, metaclass=ABCMeta):
|
||||
start = self._floor(start)
|
||||
end = self._ceil(start + size)
|
||||
|
||||
self.cpu._publish('will_unmap_memory', start, size)
|
||||
self.cpu._publish("will_unmap_memory", start, size)
|
||||
|
||||
for m in self._maps_in_range(start, end):
|
||||
self._del(m)
|
||||
@@ -806,15 +843,15 @@ class Memory(object, metaclass=ABCMeta):
|
||||
if tail:
|
||||
self._add(tail)
|
||||
|
||||
self.cpu._publish('did_unmap_memory', start, size)
|
||||
logger.debug(f'Unmap memory @{start:x} size:{size:x}')
|
||||
self.cpu._publish("did_unmap_memory", start, size)
|
||||
logger.debug(f"Unmap memory @{start:x} size:{size:x}")
|
||||
|
||||
def mprotect(self, start, size, perms):
|
||||
assert size > 0
|
||||
start = self._floor(start)
|
||||
end = self._ceil(start + size)
|
||||
|
||||
self.cpu._publish('will_protect_memory', start, size, perms)
|
||||
self.cpu._publish("will_protect_memory", start, size, perms)
|
||||
|
||||
for m in self._maps_in_range(start, end):
|
||||
self._del(m)
|
||||
@@ -831,7 +868,7 @@ class Memory(object, metaclass=ABCMeta):
|
||||
if tail:
|
||||
self._add(tail)
|
||||
|
||||
self.cpu._publish('did_protect_memory', start, size, perms)
|
||||
self.cpu._publish("did_protect_memory", start, size, perms)
|
||||
|
||||
# Permissions
|
||||
def __contains__(self, address):
|
||||
@@ -841,7 +878,7 @@ class Memory(object, metaclass=ABCMeta):
|
||||
# not happy with this interface.
|
||||
if isinstance(index, slice):
|
||||
# get the more restrictive set of perms for the range
|
||||
raise NotImplementedError('No perms for slices')
|
||||
raise NotImplementedError("No perms for slices")
|
||||
else:
|
||||
return self.map_containing(index).perms
|
||||
|
||||
@@ -869,8 +906,8 @@ class Memory(object, metaclass=ABCMeta):
|
||||
|
||||
# write and read potentially symbolic bytes at symbolic indexes
|
||||
def read(self, addr, size, force=False):
|
||||
if not self.access_ok(slice(addr, addr + size), 'r', force):
|
||||
raise InvalidMemoryAccess(addr, 'r')
|
||||
if not self.access_ok(slice(addr, addr + size), "r", force):
|
||||
raise InvalidMemoryAccess(addr, "r")
|
||||
|
||||
assert size > 0
|
||||
result = []
|
||||
@@ -880,7 +917,7 @@ class Memory(object, metaclass=ABCMeta):
|
||||
m = self.map_containing(p)
|
||||
|
||||
_size = min(m.end - p, stop - p)
|
||||
result += m[p:p + _size]
|
||||
result += m[p : p + _size]
|
||||
p += _size
|
||||
assert p == stop
|
||||
|
||||
@@ -921,8 +958,8 @@ class Memory(object, metaclass=ABCMeta):
|
||||
|
||||
def write(self, addr, buf, force=False):
|
||||
size = len(buf)
|
||||
if not self.access_ok(slice(addr, addr + size), 'w', force):
|
||||
raise InvalidMemoryAccess(addr, 'w')
|
||||
if not self.access_ok(slice(addr, addr + size), "w", force):
|
||||
raise InvalidMemoryAccess(addr, "w")
|
||||
assert size > 0
|
||||
stop = addr + size
|
||||
start = addr
|
||||
@@ -933,7 +970,7 @@ class Memory(object, metaclass=ABCMeta):
|
||||
while addr < stop:
|
||||
m = self.map_containing(addr)
|
||||
size = min(m.end - addr, stop - addr)
|
||||
m[addr:addr + size] = buf[addr - start:addr - start + size]
|
||||
m[addr : addr + size] = buf[addr - start : addr - start + size]
|
||||
addr += size
|
||||
assert addr == stop
|
||||
|
||||
@@ -990,7 +1027,7 @@ class SMemory(Memory):
|
||||
self._symbols = dict(symbols)
|
||||
|
||||
def __reduce__(self):
|
||||
return self.__class__, (self.constraints, self._symbols, self._maps, ), {'cpu': self.cpu}
|
||||
return self.__class__, (self.constraints, self._symbols, self._maps), {"cpu": self.cpu}
|
||||
|
||||
@property
|
||||
def constraints(self):
|
||||
@@ -1037,31 +1074,37 @@ class SMemory(Memory):
|
||||
size = self._get_size(size)
|
||||
assert not issymbolic(size)
|
||||
if issymbolic(address):
|
||||
logger.debug(f'Reading {size} bytes from symbolic address {address}')
|
||||
logger.debug(f"Reading {size} bytes from symbolic address {address}")
|
||||
try:
|
||||
solutions = self._try_get_solutions(address, size, 'r', force=force)
|
||||
solutions = self._try_get_solutions(address, size, "r", force=force)
|
||||
assert len(solutions) > 0
|
||||
except TooManySolutions as e:
|
||||
solver = Z3Solver.instance()
|
||||
m, M = solver.minmax(self.constraints, address)
|
||||
logger.debug(f'Got TooManySolutions on a symbolic read. Range [{m:x}, {M:x}]. Not crashing!')
|
||||
logger.debug(
|
||||
f"Got TooManySolutions on a symbolic read. Range [{m:x}, {M:x}]. Not crashing!"
|
||||
)
|
||||
|
||||
# The force param shouldn't affect this, as this is checking for unmapped reads, not bad perms
|
||||
crashing_condition = True
|
||||
for start, end, perms, offset, name in self.mappings():
|
||||
if start <= M + size and end >= m:
|
||||
if 'r' in perms:
|
||||
crashing_condition = Operators.AND(Operators.OR((address + size).ult(start), address.uge(end)), crashing_condition)
|
||||
if "r" in perms:
|
||||
crashing_condition = Operators.AND(
|
||||
Operators.OR((address + size).ult(start), address.uge(end)),
|
||||
crashing_condition,
|
||||
)
|
||||
|
||||
if solver.can_be_true(self.constraints, crashing_condition):
|
||||
raise InvalidSymbolicMemoryAccess(address, 'r', size, crashing_condition)
|
||||
raise InvalidSymbolicMemoryAccess(address, "r", size, crashing_condition)
|
||||
|
||||
# INCOMPLETE Result! We could also fork once for every map
|
||||
logger.info('INCOMPLETE Result! Using the sampled solutions we have as result')
|
||||
logger.info("INCOMPLETE Result! Using the sampled solutions we have as result")
|
||||
condition = False
|
||||
for base in e.solutions:
|
||||
condition = Operators.OR(address == base, condition)
|
||||
from .state import ForkState
|
||||
|
||||
raise ForkState("Forking state on incomplete result", condition)
|
||||
|
||||
# So here we have all potential solutions to address
|
||||
@@ -1094,7 +1137,9 @@ class SMemory(Memory):
|
||||
if condition is True:
|
||||
result[offset] = Operators.ORD(value)
|
||||
else:
|
||||
result[offset] = Operators.ITEBV(8, condition, Operators.ORD(value), result[offset])
|
||||
result[offset] = Operators.ITEBV(
|
||||
8, condition, Operators.ORD(value), result[offset]
|
||||
)
|
||||
return list(map(Operators.CHR, result))
|
||||
|
||||
def write(self, address, value, force=False):
|
||||
@@ -1109,7 +1154,7 @@ class SMemory(Memory):
|
||||
size = len(value)
|
||||
if issymbolic(address):
|
||||
|
||||
solutions = self._try_get_solutions(address, size, 'w', force=force)
|
||||
solutions = self._try_get_solutions(address, size, "w", force=force)
|
||||
|
||||
for offset in range(size):
|
||||
for base in solutions:
|
||||
@@ -1119,8 +1164,8 @@ class SMemory(Memory):
|
||||
|
||||
for offset in range(size):
|
||||
if issymbolic(value[offset]):
|
||||
if not self.access_ok(address + offset, 'w', force):
|
||||
raise InvalidMemoryAccess(address + offset, 'w')
|
||||
if not self.access_ok(address + offset, "w", force):
|
||||
raise InvalidMemoryAccess(address + offset, "w")
|
||||
self._symbols[address + offset] = [(True, value[offset])]
|
||||
else:
|
||||
# overwrite all previous items
|
||||
@@ -1167,14 +1212,19 @@ class LazySMemory(SMemory):
|
||||
self.backed_by_symbolic_store = set()
|
||||
|
||||
def __reduce__(self):
|
||||
return (self.__class__, (self.constraints, self._symbols, self._maps),
|
||||
{'backing_array': self.backing_array,
|
||||
'backed_by_symbolic_store': self.backed_by_symbolic_store,
|
||||
'cpu': self.cpu})
|
||||
return (
|
||||
self.__class__,
|
||||
(self.constraints, self._symbols, self._maps),
|
||||
{
|
||||
"backing_array": self.backing_array,
|
||||
"backed_by_symbolic_store": self.backed_by_symbolic_store,
|
||||
"cpu": self.cpu,
|
||||
},
|
||||
)
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.backing_array = state['backing_array']
|
||||
self.backed_by_symbolic_store = state['backed_by_symbolic_store']
|
||||
self.backing_array = state["backing_array"]
|
||||
self.backed_by_symbolic_store = state["backed_by_symbolic_store"]
|
||||
|
||||
def mmapFile(self, addr, size, perms, filename, offset=0):
|
||||
"""
|
||||
@@ -1196,11 +1246,11 @@ class LazySMemory(SMemory):
|
||||
- 'Map already used' if the piece of memory starting in C{addr} and with length C{size} isn't free.
|
||||
"""
|
||||
# If addr is NULL, the system determines where to allocate the region.
|
||||
assert addr is None or isinstance(addr, int), 'Address shall be concrete'
|
||||
assert addr < self.memory_size, 'Address too big'
|
||||
assert addr is None or isinstance(addr, int), "Address shall be concrete"
|
||||
assert addr < self.memory_size, "Address too big"
|
||||
assert size > 0
|
||||
|
||||
self.cpu._publish('will_map_memory', addr, size, perms, filename, offset)
|
||||
self.cpu._publish("will_map_memory", addr, size, perms, filename, offset)
|
||||
|
||||
map = AnonMap(addr, size, perms)
|
||||
self._add(map)
|
||||
@@ -1212,18 +1262,18 @@ class LazySMemory(SMemory):
|
||||
# size value is rounded up to the next page boundary
|
||||
size = self._ceil(size)
|
||||
|
||||
with open(filename, 'rb') as f:
|
||||
with open(filename, "rb") as f:
|
||||
fdata = f.read() # fdata is a bytes now
|
||||
|
||||
# this worked
|
||||
fdata = fdata[offset:]
|
||||
fdata = fdata.ljust(size, b'\0')
|
||||
fdata = fdata.ljust(size, b"\0")
|
||||
|
||||
for i in range(size):
|
||||
Memory.write(self, addr + i, chr(fdata[i]), force=True)
|
||||
|
||||
logger.debug('New file-memory map @%x size:%x', addr, size)
|
||||
self.cpu._publish('did_map_memory', addr, size, perms, filename, offset, addr)
|
||||
logger.debug("New file-memory map @%x size:%x", addr, size)
|
||||
self.cpu._publish("did_map_memory", addr, size, perms, filename, offset, addr)
|
||||
|
||||
return addr
|
||||
|
||||
@@ -1245,7 +1295,11 @@ class LazySMemory(SMemory):
|
||||
:param int to_addr:
|
||||
:return:
|
||||
"""
|
||||
logger.debug("Importing concrete memory: {:x} - {:x} ({} bytes)".format(from_addr, to_addr, to_addr - from_addr))
|
||||
logger.debug(
|
||||
"Importing concrete memory: {:x} - {:x} ({} bytes)".format(
|
||||
from_addr, to_addr, to_addr - from_addr
|
||||
)
|
||||
)
|
||||
|
||||
for m in self.maps:
|
||||
span = interval_intersection(m.start, m.end, from_addr, to_addr)
|
||||
@@ -1262,9 +1316,7 @@ class LazySMemory(SMemory):
|
||||
self.backed_by_symbolic_store.add(addr)
|
||||
|
||||
def _map_deref_expr(self, map, address):
|
||||
return Operators.AND(
|
||||
Operators.UGE(address, map.start),
|
||||
Operators.ULT(address, map.end))
|
||||
return Operators.AND(Operators.UGE(address, map.start), Operators.ULT(address, map.end))
|
||||
|
||||
def _reachable_range(self, sym_address, size):
|
||||
solver = Z3Solver.instance()
|
||||
@@ -1301,7 +1353,7 @@ class LazySMemory(SMemory):
|
||||
from_array = True
|
||||
elif addr in self.backed_by_symbolic_store:
|
||||
m = self.map_containing(addr)
|
||||
from_array = not m or 'w' in m.perms
|
||||
from_array = not m or "w" in m.perms
|
||||
else:
|
||||
from_array = False
|
||||
|
||||
@@ -1354,7 +1406,7 @@ class LazySMemory(SMemory):
|
||||
if ptr + len(data_to_find) >= mapping.end:
|
||||
break
|
||||
|
||||
candidate = mapping[ptr:ptr + len(data_to_find)]
|
||||
candidate = mapping[ptr : ptr + len(data_to_find)]
|
||||
|
||||
# TODO: treat symbolic bytes as bytes that don't match. for our simple tests right now, the
|
||||
# bytes will be there concretely
|
||||
|
||||
@@ -7,7 +7,7 @@ from ..utils.helpers import issymbolic
|
||||
from ..core.smtlib.solver import Z3Solver
|
||||
from ..core.smtlib.operators import ITEBV, ZEXTEND
|
||||
|
||||
VARIADIC_FUNC_ATTR = '_variadic'
|
||||
VARIADIC_FUNC_ATTR = "_variadic"
|
||||
|
||||
|
||||
def isvariadic(model):
|
||||
|
||||
@@ -12,7 +12,9 @@ class State(StateBase):
|
||||
return self._platform.current.memory
|
||||
|
||||
def execute(self):
|
||||
from .cpu.abstractcpu import ConcretizeRegister # must be here, otherwise we get circular imports
|
||||
from .cpu.abstractcpu import (
|
||||
ConcretizeRegister,
|
||||
) # must be here, otherwise we get circular imports
|
||||
|
||||
try:
|
||||
result = self._platform.execute()
|
||||
@@ -27,10 +29,7 @@ class State(StateBase):
|
||||
state.cpu.write_register(setstate.e.reg_name, value)
|
||||
|
||||
setstate.e = e
|
||||
raise Concretize(str(e),
|
||||
expression=expression,
|
||||
setstate=setstate,
|
||||
policy=e.policy)
|
||||
raise Concretize(str(e), expression=expression, setstate=setstate, policy=e.policy)
|
||||
except ConcretizeMemory as e:
|
||||
expression = self.cpu.read_int(e.address, e.size)
|
||||
|
||||
@@ -38,10 +37,7 @@ class State(StateBase):
|
||||
state.cpu.write_int(setstate.e.address, value, setstate.e.size)
|
||||
|
||||
setstate.e = e
|
||||
raise Concretize(str(e),
|
||||
expression=expression,
|
||||
setstate=setstate,
|
||||
policy=e.policy)
|
||||
raise Concretize(str(e), expression=expression, setstate=setstate, policy=e.policy)
|
||||
except MemoryException as e:
|
||||
raise TerminateState(str(e), testcase=True)
|
||||
|
||||
|
||||
+140
-139
@@ -1,141 +1,142 @@
|
||||
from binascii import unhexlify
|
||||
#./print_random seed=0000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
seed = '0000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
stream = 'f795bd4a52e29ed713d313fa20e98dbcc8d1e5115952f7fa3738b4c5ceb2b09a0d9cc50d16e1bcedcf6062099d20837e6f39e0cb767c0debffa00e54'
|
||||
stream += 'e89ba679f60865f512d37070e748b00ea5a090cbe226cbffd135f48cee4e1ae66923369a97729ba14591a4908ae16fb07a3226bafa494f2501e95dee'
|
||||
stream += 'd7149ad824c0f2d5cc23f0059dc83fc17efbb4409773bfaed54d441d46768ed7ed878408f8de121ed411a6dfa262c78d37f7366e1803a4c56e1e0b76'
|
||||
stream += 'b815690de5f9908eeae544e7988698cbebdf49da51c9d88a729cfc40a2dfe92037a6c1c5f9a8b72642a5c725f94415a153ba4f036899c78fd1fcf093'
|
||||
stream += '8b2536004aaeaf6e626ee160770f3051fb3b574a094b620c0fa4804afef448ec9437b8a6faf9da30908a7bd0d5606d832e5dd0ec810578aa598e9914'
|
||||
stream += '3b965f1e0b9715c49d0fe0f691a7106eaba43bfd1c185678b407300f85c28d609c1987d1f5d23968be55982f5ea3eb8f2370a8040edd6a385b98f6f6'
|
||||
stream += 'f28b271533cab1eb1653a4b8023c323282299b445a8fca5aed2b2cc6a0740dee3dc6bf5171a317a392f87dae4a1cbe54cfa6c4c1794464c589f51daa'
|
||||
stream += '8fa81d17b04811ce430088f790502e091d022994317674358bbae64a347d7e621802abdbe6a1ea80f1dc2f1d3dd1124448ac7d14aa11a235b1e2cb44'
|
||||
stream += 'f50a8e379e015c3495496f2488da8b4aefb46f67672c46a86c7d4e45855eaa7daaa63ceb17452f433deb144def280c82cf5d3ac2d1cc02d56a8a0916'
|
||||
stream += '321b1606eca356d472cece5b95699af02e6266ee61f3c51ab3817db1043bd8d8ff4b34c36ee2da1faf97c97bcfa967fbe72e363533baa28da46f9cb6'
|
||||
stream += '87cab374957e45d46561aa803c70119ebdc7d3e5ec4a4d771ceea8b933d4c3e2616c38e83a215dca5767c9eabcc2a5622957c6c785a1890415057680'
|
||||
stream += '932a46e439e3ce44b09f700f8fac58c422e865ed519725da7c475454617dbb5678dfcaa6d093aa133ff2e4a74163ce1b1cb3d99dbe445f39b997249c'
|
||||
stream += 'ec5f3fb17923a310eb1e62cbb93d3a3e84f3d20169483514baed671567467749f886b89a75c2c6291b85139dc4f95046a3e1ee94d015c6d73ed548c3'
|
||||
stream += 'b48131f6e0660b646cb2fb07db0284f41ebf66068f98fd5508ee57df4626297ad60b4bafef0e25958e223866efab0e7e12fe9acb66dcc3cb002bd967'
|
||||
stream += '517f5c4a632828290300ceaaf116e35025953c4c7c8ccd60587ab9c914184a701f81fa21c403b2a69b0c935d5e9a62b986fbb6a3417ee94f2ebe9959'
|
||||
stream += '5a90b5637952a4408c190d77afdc581650661c00390b029d0801e4f616696cbcf9d91d67a147fcfd465a117ac2f9208fd98942d4602548bab0a2ceea'
|
||||
stream += 'd333d0ce7daf8451f8d5812bb54000a0e0e1a939ad6384d5b9a8fe84c214208df0723bc68a72f88558cd7e0b6c47fdc10a2b46e77a7b6d65ed4e9bb2'
|
||||
stream += '93862570234aadd3aebecc4c1faa63e3819d114e67c8aaf5d3e0c286a287e616b9d156e965937f61aa5f6b64de1f98198aec6478e56c72f881c2052f'
|
||||
stream += '7a42f30f281dbec3474f1095cf27308a44a1e80cab0469735fa26dfa923229c89b6b7da47ecc051cc87767455e886ffdd6ce9271923a5a198d79de6b'
|
||||
stream += '1ac0f863434a9815ea8b8efa7102f86bca7774d528621bb2e4cb2ff93a43c8cbe15c6d9018097a0eb042e62ff0fbc7123dfbadb72fcd9a13a57bb0ae'
|
||||
stream += 'b91bba5cf321b5b619ad530f26524455b5855b2094c3f860b9766a2f0fa1830920ac1e483fe8f564438fb9e271d98a3890a862fecfd06e32dba264ce'
|
||||
stream += '0afc5f0aee6468a41c28d3321cb09f0819617ab9a5ef8f9a461ffe708467dec83885112d92d59fb1f8a2815dc790a338db4bbca3d673b2390842a637'
|
||||
stream += 'f9b52023681931951b09d9c7c9c904be43e103e80ad78f0518e7a393823796d9b33a6480fd77c66947d1b6caad2030949fd7b6cb5b6407dfcb81386f'
|
||||
stream += '0043a489b2946b5d05ec041b26cdea54e8db3cc2c84fe193d98cb85b96bb57d42cb5a39bd66c69aea2343d648c360f7877b6c8c55fd4322f401eea08'
|
||||
stream += '796a58b8e30727a660fc23669f2b31bf242d11af106c64716c7a76596d2c4e02bc71d9c75ed4d6e5861396282d2cbe0c30ff306e4a129f89a2ad2c7e'
|
||||
stream += 'e78b2bf091a4eb1569ccfae7e0248e2f594c5138d8d925048da3392c0450063d21aa400ade9c8be384699a1f315de8d4e57de38d698bbb3d6d7012ed'
|
||||
stream += '5c6b75b96bd1923203f1726091182001ba92270bb6d0e7d0822382018baff5d14c1dfc477d7142942c346b384e8b008cef49bc86ec9aa4275ac5bf3e'
|
||||
stream += '1e707b18b058fb4cf39e97cf443468f41ea45ef1bbbe412c63952dd60ac06f50b0df49d0d8ad1d55cf9bdbbc50e1a0322ba0ee42ff549aa07f767642'
|
||||
stream += 'e7e8825ae78604b32d9caaae0c1e687c617d722fc2b1215483cef045e67913e95ab9f5f4871430c3b8bd7544dbe6ec30a07f44734aacb854ab51f766'
|
||||
stream += 'ca39e08fd4ad7577bf59720a2a0957eed80247926b97370f8fc7dda96c735f49cad558f91f0cdeeed8564a22cc0063c62cae4b89e04ee66116708fcc'
|
||||
stream += '9eff75d7098cb1b689d8dc5b67c165270b6733e6ea4c199d181dadab57bf9aeeb41725d57061c349085c4611e298b51a926196a8a97f881698dbf5ba'
|
||||
stream += '92291cb10d0d0dbecee89d11f3bd29e52835efb49d4c405e5b7576e501c3c1301439a2ae505fa650b7b1a7d1bc0499bec64eaf0d3dd598d22e945173'
|
||||
stream += 'dfd878de05c940d0c48f47ee8ce0b401d4fd20c852c1b993d4c657eead9cdc81bb7571c5803e51fb0a30d73d0d45cad33b94463d5d4cbb33f70c3bd0'
|
||||
stream += '71216fd8dfea92b0bf3478bcce03768c8d9ab17138b6cb6344d9d10f9545fe72dbbb598fc5a508ca56bbb7a8ede7a181fba35cd6926a71ecc645d2e4'
|
||||
stream += '991426d855897fc49b0e6a07296b0fb9d57c68fe8cf474484f38fb6481149456790b1cf20e7b3c65db03d2952a1baf637c32bb48b69d80593a1b5dac'
|
||||
stream += '92f6abc4024cdade6e752c4cfc9aa0d5e14394ade464bd29bff420a87d4fccd36d84130f0e232bbde675f5d96e7a049acd95a4255cbfd64247ad3225'
|
||||
stream += 'eeb6f6cbce17dcca1abd48303de27852a114411dbe65e7173e63031caaaeaa44e0263cb1d39fa707f7a5d744ac3a1ff955490494dd0d25f4d425fec4'
|
||||
stream += 'ad362c0ee4159c7cf12abbf2374988dbec25665365940ef2ce945757c2e3e381037b74c23c0bed6865831d94cef342b669bc3546880e93a3a693b638'
|
||||
stream += '62e587d125fcddd3d84c243e3cac12543b53345a29f14a9e0c7bd5ab59cc9622192224b626df25dff1b348505435d3c7e6321c98542f71deca0620c4'
|
||||
stream += '7790dce4a64b0e7366a1f27382f1465695047d51631d109125c18e85d78cbe7a892318b7e83f8290b80694fd48d264dd4182aab1d1ee0a26934c7c5e'
|
||||
stream += 'ad35d8121aee52ae734ab1cd895368982c6baaa76ec024d53a5b0cf05ba3a1058a3fd50ae9f22d021009bddf9b244da70a14fee233b4525c26586513'
|
||||
stream += '5559465827799b3fd280d965202034e0ce1f5bad6eb26182492f76059a103a137281baec82e2c07cbbf238164888a80e181c39953b4cf47c5315f72e'
|
||||
stream += '1d322a39933dff08fd51a2c7c79f489265b7bbedc5fe03d43d7b99fabce55fe8016117c6b64d20f16c014932a2bdaaaf749e3ef9299bbd6a220e6498'
|
||||
stream += '4182220218b6d84685db558c0c58ed114a4f3efd2a4d244db4b2382c3a0448cc0ce622d79fe57293ff2ae65fa52384b7596b23f4044935782e471218'
|
||||
stream += '6ab0dc8ea367129e091367845166597e3f86ecd5b823dd1a2475d72e84d95f78101d5f4d1cac9efe31a41138b181856514c4440aaaf0f9595eb01696'
|
||||
stream += '9670dc3afeca319fd5429b08a9d200203b361d81578fe27e78fb94b87dd88e893163124fb2ded4d5d20128df985b16fcf63bee8d3670534d67a282ea'
|
||||
stream += 'c9a13fd906b4370ed2ebd81320c7cdf3c7ec62967696cb859927517dd3c8fcdd51a9f2276494d76beccd469f00034ab4f25b5e84d7c6cb36b23f51ba'
|
||||
stream += '7958294f14fe9f8188d4a90ba3fccf0b40950c73ab12e38561830e522c461f8d37e61ada7d2f89d3c9bd9d89f3bc86736db111f5cefc97e64073fdf2'
|
||||
stream += '8af9967033c7dc8598c09c42afd7fae6a83cf54acb989853c3ee447501eb5739c9e1be3d81d7f9594fffa1166e44fab0c37418fc7102a42cbc56cc00'
|
||||
stream += 'e53eb68d3579a0258b65c6918d43340b4b6cd9b4a810a55f97521a7d2a6125596ab309c4e02ba5cb0d0e75fc153fad7fe325a4eb270d0884b0d2f50d'
|
||||
stream += '45cd54fb4026af2e538c5b8732f7a9d5dc5f763cf0d1f33dea2f9e822e0bf90745b0992d6667822390eef8826ce01ab1a06fdccf093c937dccea66ed'
|
||||
stream += '2ec32e6dff20c9e3cb231bdaa3c57de6cf2777e6ed5e6c396db3327b58588c97d4e4ea4fd6980e69e7366dad233947a5952c6159e3e1de101d821d72'
|
||||
stream += '002c427f9de3ebee1abb65ee4154ae4efba21c93c28b2b763722325f63ea64eb4cb52c58943b85001adad44f9810ae04fe6e0695d62fbfc013bfcb00'
|
||||
stream += 'ce057d97363acb0a48eaad48cf7d1a1801125eb6a042b25765a828f025e5ff788d7bfdbebf849bcb7bd5fade1497a72986c7b9f11548185a5c1effa9'
|
||||
stream += 'afee048920e0173ee5211877ea410ece0254c51b6a1913041250c84771d966d07198a6654cd47be644c5d24785267b62fcb58a7e7849faca56fc08a8'
|
||||
stream += 'a76e01f76e0ecfc7c2ae507b54f39c8a6389227b37fe005838dee1b1f64d41a7dc1b2a6ae34bec0fca5fd98e04566e5192b99d4a1bda0040328509be'
|
||||
stream += 'b9150abbe72c9da0a0753eddad11e2f8a9b823930fb41082d6ef4198c392e54c59b3e11211f10867870026dabdf9265e2e98e667bfc6c718907de25b'
|
||||
stream += '72ce2aa9360fbc4fdab77de070a286976be4c9563edd9b7b8744032ce454e1e24569d2f778cdad6de4db49acdf6becd9aaf97dc439355d0cb79bbf64'
|
||||
stream += '5ac5d93fc8952070d7d9a1dd9a0b7cde0cf58283ea0d155d88996ddd4a89c1ff6a4901bf3335665b2090e1e8857f59b52f94c0c0af49d8605587ce23'
|
||||
stream += '77007071d78bd2da3d6f500b007c006d785eda6060d061d72ca2fbbfab7ec5e76ebf1473a42d66ad36df15afcc575f42e3cf061a380b9609cd7ef1b1'
|
||||
stream += 'f3bc06c5b76a49082020bde388f87b6d49a677ead17611e33de4fa0476d367df03d647ba1c84c3678b5d59aa63fe6319e5998ab7b5771db480d48d7d'
|
||||
stream += '1645ca0979183b38966f9dd6ff9f2cc323f1d78ccf5705e460ccbeba70da1917b2b5a6132a548d0cfc254159733f8666bc4beddddb0eb2dc9bc6e497'
|
||||
stream += '30bf861c8c7370d156cc9789f2547bcf2d15c0429aa20e98769424ac08388ae7db582586626c71e2085f02375b25a41a7a96576ef222a53003a1e3ae'
|
||||
stream += 'a24fe80c99b5e42ce826b788c806049f0d67fdff86622a2e79f2a3adde81b3e8c57b9dbaecc682d3caaaa68df97cc5e59fa3b72d64b24f4252e3fc3a'
|
||||
stream += 'a7d1adb481c397a38400379b4ccb225cbaba2953a5dca1b1cb786b01dba7ba6f1cd70c527a6cc38de538a5694013951b11c4625d7a35298a84cbb644'
|
||||
stream += '5362a93173938c6960524e6a969912d2315f20a069daa75851538a62c6c5867385b08f4418c024a5d17f69444ef9fa4a34994126356e2dcdf491a58a'
|
||||
stream += '8c72418a15906c5c88a88cf6454fe5fd1d405229749384f8e3c10d3786edb29746bc087c956d01c63e2b13b4bf280959218be9b72e3a78589378086b'
|
||||
stream += '8e06420f6420a2090f56f40c29839eb8925b4d7580fd912601caf750d5e05eb73820153a96cfdd6c771359e1528a81561021677b0046f521403218a3'
|
||||
stream += 'd51f917de2ea57952d610d8e8ece093385cd84e4aa1d4c0b21df558055f9b46b59e062f538bc54475e96cb7530a99f2533ec9a50fabc9b3636e7d50e'
|
||||
stream += '940380a31521fdd6643fa56ae8d4359580696d80291b07a210f582b107224675eef361af58ede40b2c040d381e088919a53b137c1d813fd0e6561671'
|
||||
stream += '27d2a097101396fc9b8c8f692792e1004f84a1f5400d2f20474557aad75588e696980c580674fd352ef766e6cb9a8a19e73bede400b625c017f3c630'
|
||||
stream += 'd07ea1c54ca8b87b517942b66e9abba802745dff98cf8a8fb8839dfd1b257260590d330890a557d797a33f14374aa916fa7b5db4bc205de64184abd6'
|
||||
stream += '6533e5cea0b5e1141ef8bfb380f4267b50752dac5f8753209676586207891da31ae218362f91ed74cc5771ba5f2b84fe0e69d7c958bf58178ec0e047'
|
||||
stream += 'b8cba4d1d39b8be156dc7a43717a668c8539f2f5cb640d67c1c1d445696bc4382485e0b5b5d96fd6dfe9261ca9b8e6fe874ee09d33bbee22a16fa13e'
|
||||
stream += '9df931faef02b3da9a25b977c8c01ac785b4c9a5eeb9a59f78c3a976d6a21df9cd8fd122b99defd367f2fc3375f91d8125b2bafeec235b95d5d2a3e7'
|
||||
stream += 'a096dd20a36083245c74371b8250346d27e1c115623eea2c66f9a4ec33e6401bde4c68460ab0708866340a15833b04776d4199d71f3c296b5a409ac0'
|
||||
stream += '053c29fe94e799e3a43435cd719d23b10d11e3327cde17cfbcb28da13b7fb7db9f038cd9509a0c9117702be57f16fd93f8f800f3c5ca816491cb0ba3'
|
||||
stream += '49b65206d768a6661f349b7ae3179462b351e0c3e3628f72f02892ae31a2e72bd8db023984f35664ea8fef4bd2f17cd0cec5c4fca7c35efe7552c583'
|
||||
stream += '582781b107152edfd454b006a0f9d0cc39aa77b43c6436be9ec776b4b7d7552be92d664a2271c8d6593c5ac9d99c40b443aba812f02c77a8bf7f51dc'
|
||||
stream += '91e13f9a933a8cca0aeee6af0195c5cda1cd2a806e52aa944cc72b938352da2f524c6a3504f355a88a142ab50fdfefa3803322acc86d6f66c6120c68'
|
||||
stream += 'ad14b2f0940b8b79ea3b5231c631e6186bb07c4ef93fbcd0237ab06aa069e56db78337458f767166fa5de89b5475ad75e06485c02f103404a404619b'
|
||||
stream += 'c6f38dbc0050fcf553e153aa7e19367df394437dcbd91c37d8020587530d1a45b9a5ed76a66491d5b308a185d556e3abb06c8744888a8d1ac57ac490'
|
||||
stream += '5ee6b076fcf552f33d3efd32b8857dbaea42397e6cbf2c706d6d674f52fc8ea193a093008d7c7dab0440b97ac95223a3fd80d0ddb1eecdbfc3046545'
|
||||
stream += '2da89c129f15d9a165b83ba4edf41064d4a3006adb4167158cebc5e94c96a1cd20818bbddfcce2d62f67e36e1c6b9150ea01ad2c834257ce01dec049'
|
||||
stream += 'b2bc5d1528feb7400a353b49926944cebb1286fd3a7829c76a948e45d50119bfb8fcdfda8380ad7bb4950b6fe5fccbb50939d6fd801a00e157a693d0'
|
||||
stream += '4938dc498538c6f9a56157ff4cf1efa517fa173904d16b9b5bb3289127fbdfa30a4a66d340c87f04b53c1ce407dd0d8f3a63b8ce9bb211881b5b9808'
|
||||
stream += '6c6848cfb9635bddeebf6bafc74771f7b2681f3ec6d32fc31a1bd9c8284714d88ccd2a4a8cf6961f0c7c83e115ad982606cf42707202ba0860e83fbf'
|
||||
stream += '9a9a7b594d1a60ccc6bf6b637ab95e461decac0810b7eea88a9e25c8cfb2b9d2e0a83802a70bab37b627ff7f98375a260a5ac2cb0637294956874c28'
|
||||
stream += '3b97cab4616c8e297a7652bb8043c891e5242a7fff99becf6ee8a8090b8f96b7667b666679d6f25f5a52c42ce02100cd4cd2dd7a1e1ce493fdb41f59'
|
||||
stream += 'fa18f4e35fd6cd74f3b005ca852afa3cf086cc0b90291711e6fc0c186231dce1be644a599435b8872f7ab8c63a38d92db5cc944ecc7b3a96dc603bc8'
|
||||
stream += '7fd09edac53f19104b74b215178045053dc4e1d1e919e44e920f49f4e73417be50a9ef64d54f65feea6207c7caaaaa8feb9608fe8de3c473d134a092'
|
||||
stream += '42f52f146eea15844e7652aa5036c7254fd2006d8b57074cf5ac2f8a916592b6ad926910b22ade6e7797ac5be196d10bb5851719c8dc547657180647'
|
||||
stream += 'ff701df8f9c574b29715de17c16f57d26a084f83bbff21b72bc67af4fc505f7879cfe4b4132952eef7e6a111425b20507e45f4478ce99882db0235b3'
|
||||
stream += 'b68b7bc0ec7f2a20e9291c82aec433d704bef5cb71406ac9172988df7aacba40eb257c2c4e2fddfab8e9a8ef884716a26e96848d9819841ab0855c2b'
|
||||
stream += '76aa319c170637afa8b0c86e2b3493b675b4b83ab684205c67e1abce31e8e486a8406493251921ebe834e32a49874240289c31563c39be5fa7d73e3c'
|
||||
stream += '6dc4060943e4714f477bf785c2e8bc73a9c6cfd12210e7d4c13deee8cb92f3ccff3131af4913b7edb3dc5ea5e5969dce60522dc5bfc1b250cf53550b'
|
||||
stream += '4cd929b10a37d91f8bc9dfcd43fdfff1e72689aa415204924b1d73a047a92f69e02f286ba928fba152347ee9e459e2cb51eef3202c5281c7c8dd53f8'
|
||||
stream += '507e34f9edc0eb86dfa258f4ae6971bd20da0ba1b3f9223dada9a8e05634153611475a11ce2b670ba4d01dd8782925748c31850659d23d67d28884a2'
|
||||
stream += 'c854404a04dba8a41a8c98e06f2c0a280427e1beeaae44fad98b077d900b6d9f55ddc59ce9543f5c4b7be31e3e3491e485ace8047e65cf5b51684f87'
|
||||
stream += 'dad986729e7a6b2f94703109713dbd77454ed950882835d705baf9997d59ead5b0adbaa734f2539e990ae0672eb83a290ad95e6f1a4330a0f6bcb7b3'
|
||||
stream += '41d4429c223091427adaaf48c69c8c59011d7dad040a2ce5816085dbca8a532ae267c77565665044e1c905a8617b00f8e7d3ce393845f51fd58ae160'
|
||||
stream += '43c72f82f5e2bf2486b6ceab75fc4bd5e02f217fae2cb80c035d7289af39537699a844e9e4807fb01df4a2c87aacaeba9fbc02735a9ec6c013f907e2'
|
||||
stream += '9e6c158615eab92c7cc12265a7dc0f060421fb4ca70dafc80dce47daa42af4432248f995c2f446e31efdbef26aeb6eaae9442275aba397364a83882f'
|
||||
stream += 'f45a05d11391f4499a22783bca612dc61c9812063c24a8b9b2cbe16af31931f63091888eb8bca0b71861a92000ffb72bce0206fd730cd35267f74d7d'
|
||||
stream += '8d449ef5dc1c5100df72b0c921c48c32a91c0b7b409a07103eae14991e15f870252af33fb95a3147c75871ff7de2e3ef229d95fde60fb17960d1af8c'
|
||||
stream += '975c80646f09326e7f1bf7ad4746c0a97abf813936b782320f32dad425cbd6299788ef7bbe81ce9ddbf07833949c5380e932d73509dba78efd8682d5'
|
||||
stream += '066437e36d0e2febd7d5c0fd47596f5ad0db378af06f456317965168a4cf605de5c94f65107fe2c32d5cbb3237b6651923056ba699fb757df6da86d7'
|
||||
stream += '79a899f668ab9ce6ca5a64f85bfe2a00153403d30c543a7ea68d1c475945a4b138b9d68ba0829d736314c965845e9022738c65d20388f21a7bbc1077'
|
||||
stream += 'da441d9927b354298d183517d7e0c7304bdf14259cf56837b23fd9c78dad0d47efd021c29be31b360faa0b1cc00e96bd3cc015a92fff2d8a4b86c133'
|
||||
stream += '57fd9b5179402e69f19736401f7a3ca75cecd142eb2dcf4f9fae4d5291fc9cbd1d59ea355eeb6a761092f31b013eb31ad4bd35775b48a913b336f0ab'
|
||||
stream += '83105461586fa1a3caf93ca9141659eee18974674a68735e5e27d3bd6aaeb3e0a66d5998d95f125d33a53baf84827a689e9fdec36cf9445af99f99eb'
|
||||
stream += 'a692f7dbcf1b58821e31469cc94ce0b6ce372492fc1efb6578e528f642fd5988dd206f11022683bdbd8073b79b77b34cddce81496bbfeaf627078f25'
|
||||
stream += 'b3573dfe5e37f40d251d8af2f1cf321dba17a8373642d71db1ed29db5941df01ebaddfa157a2b750b98b647af2bf785e5cc83b8088728e8a560d2b08'
|
||||
stream += '51e57e5adc719b087c082360570a9efbfa4715651078141bab8b58acb0bec9087af6db2a4abf23d800d743c8cade4f02d949742c00d2b22cfad8d977'
|
||||
stream += 'c211d5cc5d2b5c75e17210f76ab711513ad6b7e905329a76b7cc6acc5f9861be20132ffb4e0da5fc568312ad2d40edc7489fa3a190a5559e12df5afb'
|
||||
stream += '362fd3b67dab874314a804b0a510cf64cf64f479af71c106a208f05d89e21308190795deb9b179fcee275f5d77c1f0b4e1844a0d1fd0de370ee49351'
|
||||
stream += '171145dbb88491fc9f5443cd2b8065b846349fc869bf7fbada55988f57cb51c534a226f93a3c5e58b5478f073c9bb3444b81189df6346b06d8c7d380'
|
||||
stream += '1bdc417c89c3f037b2ca60469d09110865f35aba261f7f3f0241b4fd252146ddb80e416f131269e9dcf4d134a11c1c0e0f136f04bf4657204ad349bb'
|
||||
stream += 'b33442d99fe9560d1558cc20936877ef77d62771df37116ffb6c31c872de5952a19d530703a98133629a1aeeffc37b60d0587293dd43e43bcf175d57'
|
||||
stream += '09a0879454b7c7e80ebfbd3f030b2420deb0b664f8900f1939a7f611b01ad92d539d448f4a1f7cba8f9738f353fa038258666d73c4fdbff3dc122321'
|
||||
stream += '9fd3dd4820baddc7aa9e3434745938d9aa2dee71e119228ba6eef0ac014eedad9033bc28741d29a7c98c68441143fba8a8016a13068529dd135d01f9'
|
||||
stream += '1dd8ea549ea2af7aaff01c602f226a2bb29e46b8b191640bbbe4e5d31c3a04fe2d2c44992a2d68279e346d7385eac6d59eec08c2b2bf5adaf17ab42f'
|
||||
stream += '55f06eb10581d7a8b730768a784f0bbcb94222f1155f912e6424fbb51c80e833e8d8f78d2229a8fb9e51dc05dc90659e1e5b22993e12f7bbf28cd948'
|
||||
stream += '3c41ca5930763d25bf9b0d7c211bdb65310c9f617fad30b03adf303fa4a853052d5ae077f9be6ad53fe29bf6ae43b97eea9d9524b2a6bb32572f38de'
|
||||
stream += 'c080ff67a537153b373383f33056e1a59f0730dcbd99d60b2165b4c043673e747bd885d6177fd4310dde0fb0ab8ace560273cf20ccebf04dcba0f89e'
|
||||
stream += '1d24e5d1f0755fea07800de84475bcdba38078a6a04b8def1b21be0c4c80e92aa80399e4d2223eadf0458e8a1edf12745618aa6c20f0d429e7000e94'
|
||||
stream += 'c06d95376ca92ac13d9b9564c5859e86a544cad717156aa256815dd116f34c542c60804c34311ed582f6febaf633e80c7ce8f5fa34a82ac7aaa0d993'
|
||||
stream += 'c9b67ec2a44ee6dfad48fc8db1e8afb861fe750dcb324a09648d75cb527e1bd17a9bc1d60cce2b28b635bbdaa52c2fba2a5ac18737a59643bd2f32b2'
|
||||
stream += '39fbbc8ada2ffa9d6ac10276274a5823e3a866c0d98c2c0dc5334bc5102fc91bea7001d124063634d9f22955094b82f6cf82ff22a2679dbbc4307eb8'
|
||||
stream += '2d96fd6920801476ecfbf412a024445d080c1b3079f60ae484fd8214516c2dde011f5c0882f60e2c753a01f3e761ac32a5981beae530e851f4aff9bb'
|
||||
stream += 'a328e655ebc76b2c6a92c25bfd8141f762abb0d877df5563a0300e42897aee78a2c0ec05d8faa5f633148534246d12eec5a488ceb16495faec22137f'
|
||||
stream += '7abcfe19eeac83af668b54c9ca11ff3fe6bc35d7817c620864cbfcff0027075f93185197a08e3a0b8efaf70859dfa08cc3d1c7c9e7c8ee95ea469649'
|
||||
stream += '4443f318cf7475fc28cb63feeb58130fd5296a56bfcce0ae96ad1c0723edcfc08c6f44176928eca3f5dfd9610e4de5e4d129b554a5c526d38e4088ec'
|
||||
stream += '0c48ec037ed05356f2687f7a33551c3ac15772cea4ebc764549f0a18e6fc6236bfc481662968caea5435334bcf4c27c7369e0f59c0729c48538611fa'
|
||||
stream += '2594e0ad63b410d39a36760fe5dfc0105e2a87b51ffcea4eb70ef55969e59fb7ba41700d93cb590ca73335a00a333515819d9d4740b25b933f4ba4ce'
|
||||
stream += '8a65f0365072a88e478330dcd751028fc5a084ee0c01568a47905473fc5346bab0eb7c097bc73f8119ddfbff755f8b2fa1fe28982a84ffdcf921a7be'
|
||||
stream += '98ffc2a7e1cb27ad9ba45a3aaa6ee19a9ef5f1df65459df4b27eaffa87d7cef2'
|
||||
|
||||
# ./print_random seed=0000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
seed = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
stream = "f795bd4a52e29ed713d313fa20e98dbcc8d1e5115952f7fa3738b4c5ceb2b09a0d9cc50d16e1bcedcf6062099d20837e6f39e0cb767c0debffa00e54"
|
||||
stream += "e89ba679f60865f512d37070e748b00ea5a090cbe226cbffd135f48cee4e1ae66923369a97729ba14591a4908ae16fb07a3226bafa494f2501e95dee"
|
||||
stream += "d7149ad824c0f2d5cc23f0059dc83fc17efbb4409773bfaed54d441d46768ed7ed878408f8de121ed411a6dfa262c78d37f7366e1803a4c56e1e0b76"
|
||||
stream += "b815690de5f9908eeae544e7988698cbebdf49da51c9d88a729cfc40a2dfe92037a6c1c5f9a8b72642a5c725f94415a153ba4f036899c78fd1fcf093"
|
||||
stream += "8b2536004aaeaf6e626ee160770f3051fb3b574a094b620c0fa4804afef448ec9437b8a6faf9da30908a7bd0d5606d832e5dd0ec810578aa598e9914"
|
||||
stream += "3b965f1e0b9715c49d0fe0f691a7106eaba43bfd1c185678b407300f85c28d609c1987d1f5d23968be55982f5ea3eb8f2370a8040edd6a385b98f6f6"
|
||||
stream += "f28b271533cab1eb1653a4b8023c323282299b445a8fca5aed2b2cc6a0740dee3dc6bf5171a317a392f87dae4a1cbe54cfa6c4c1794464c589f51daa"
|
||||
stream += "8fa81d17b04811ce430088f790502e091d022994317674358bbae64a347d7e621802abdbe6a1ea80f1dc2f1d3dd1124448ac7d14aa11a235b1e2cb44"
|
||||
stream += "f50a8e379e015c3495496f2488da8b4aefb46f67672c46a86c7d4e45855eaa7daaa63ceb17452f433deb144def280c82cf5d3ac2d1cc02d56a8a0916"
|
||||
stream += "321b1606eca356d472cece5b95699af02e6266ee61f3c51ab3817db1043bd8d8ff4b34c36ee2da1faf97c97bcfa967fbe72e363533baa28da46f9cb6"
|
||||
stream += "87cab374957e45d46561aa803c70119ebdc7d3e5ec4a4d771ceea8b933d4c3e2616c38e83a215dca5767c9eabcc2a5622957c6c785a1890415057680"
|
||||
stream += "932a46e439e3ce44b09f700f8fac58c422e865ed519725da7c475454617dbb5678dfcaa6d093aa133ff2e4a74163ce1b1cb3d99dbe445f39b997249c"
|
||||
stream += "ec5f3fb17923a310eb1e62cbb93d3a3e84f3d20169483514baed671567467749f886b89a75c2c6291b85139dc4f95046a3e1ee94d015c6d73ed548c3"
|
||||
stream += "b48131f6e0660b646cb2fb07db0284f41ebf66068f98fd5508ee57df4626297ad60b4bafef0e25958e223866efab0e7e12fe9acb66dcc3cb002bd967"
|
||||
stream += "517f5c4a632828290300ceaaf116e35025953c4c7c8ccd60587ab9c914184a701f81fa21c403b2a69b0c935d5e9a62b986fbb6a3417ee94f2ebe9959"
|
||||
stream += "5a90b5637952a4408c190d77afdc581650661c00390b029d0801e4f616696cbcf9d91d67a147fcfd465a117ac2f9208fd98942d4602548bab0a2ceea"
|
||||
stream += "d333d0ce7daf8451f8d5812bb54000a0e0e1a939ad6384d5b9a8fe84c214208df0723bc68a72f88558cd7e0b6c47fdc10a2b46e77a7b6d65ed4e9bb2"
|
||||
stream += "93862570234aadd3aebecc4c1faa63e3819d114e67c8aaf5d3e0c286a287e616b9d156e965937f61aa5f6b64de1f98198aec6478e56c72f881c2052f"
|
||||
stream += "7a42f30f281dbec3474f1095cf27308a44a1e80cab0469735fa26dfa923229c89b6b7da47ecc051cc87767455e886ffdd6ce9271923a5a198d79de6b"
|
||||
stream += "1ac0f863434a9815ea8b8efa7102f86bca7774d528621bb2e4cb2ff93a43c8cbe15c6d9018097a0eb042e62ff0fbc7123dfbadb72fcd9a13a57bb0ae"
|
||||
stream += "b91bba5cf321b5b619ad530f26524455b5855b2094c3f860b9766a2f0fa1830920ac1e483fe8f564438fb9e271d98a3890a862fecfd06e32dba264ce"
|
||||
stream += "0afc5f0aee6468a41c28d3321cb09f0819617ab9a5ef8f9a461ffe708467dec83885112d92d59fb1f8a2815dc790a338db4bbca3d673b2390842a637"
|
||||
stream += "f9b52023681931951b09d9c7c9c904be43e103e80ad78f0518e7a393823796d9b33a6480fd77c66947d1b6caad2030949fd7b6cb5b6407dfcb81386f"
|
||||
stream += "0043a489b2946b5d05ec041b26cdea54e8db3cc2c84fe193d98cb85b96bb57d42cb5a39bd66c69aea2343d648c360f7877b6c8c55fd4322f401eea08"
|
||||
stream += "796a58b8e30727a660fc23669f2b31bf242d11af106c64716c7a76596d2c4e02bc71d9c75ed4d6e5861396282d2cbe0c30ff306e4a129f89a2ad2c7e"
|
||||
stream += "e78b2bf091a4eb1569ccfae7e0248e2f594c5138d8d925048da3392c0450063d21aa400ade9c8be384699a1f315de8d4e57de38d698bbb3d6d7012ed"
|
||||
stream += "5c6b75b96bd1923203f1726091182001ba92270bb6d0e7d0822382018baff5d14c1dfc477d7142942c346b384e8b008cef49bc86ec9aa4275ac5bf3e"
|
||||
stream += "1e707b18b058fb4cf39e97cf443468f41ea45ef1bbbe412c63952dd60ac06f50b0df49d0d8ad1d55cf9bdbbc50e1a0322ba0ee42ff549aa07f767642"
|
||||
stream += "e7e8825ae78604b32d9caaae0c1e687c617d722fc2b1215483cef045e67913e95ab9f5f4871430c3b8bd7544dbe6ec30a07f44734aacb854ab51f766"
|
||||
stream += "ca39e08fd4ad7577bf59720a2a0957eed80247926b97370f8fc7dda96c735f49cad558f91f0cdeeed8564a22cc0063c62cae4b89e04ee66116708fcc"
|
||||
stream += "9eff75d7098cb1b689d8dc5b67c165270b6733e6ea4c199d181dadab57bf9aeeb41725d57061c349085c4611e298b51a926196a8a97f881698dbf5ba"
|
||||
stream += "92291cb10d0d0dbecee89d11f3bd29e52835efb49d4c405e5b7576e501c3c1301439a2ae505fa650b7b1a7d1bc0499bec64eaf0d3dd598d22e945173"
|
||||
stream += "dfd878de05c940d0c48f47ee8ce0b401d4fd20c852c1b993d4c657eead9cdc81bb7571c5803e51fb0a30d73d0d45cad33b94463d5d4cbb33f70c3bd0"
|
||||
stream += "71216fd8dfea92b0bf3478bcce03768c8d9ab17138b6cb6344d9d10f9545fe72dbbb598fc5a508ca56bbb7a8ede7a181fba35cd6926a71ecc645d2e4"
|
||||
stream += "991426d855897fc49b0e6a07296b0fb9d57c68fe8cf474484f38fb6481149456790b1cf20e7b3c65db03d2952a1baf637c32bb48b69d80593a1b5dac"
|
||||
stream += "92f6abc4024cdade6e752c4cfc9aa0d5e14394ade464bd29bff420a87d4fccd36d84130f0e232bbde675f5d96e7a049acd95a4255cbfd64247ad3225"
|
||||
stream += "eeb6f6cbce17dcca1abd48303de27852a114411dbe65e7173e63031caaaeaa44e0263cb1d39fa707f7a5d744ac3a1ff955490494dd0d25f4d425fec4"
|
||||
stream += "ad362c0ee4159c7cf12abbf2374988dbec25665365940ef2ce945757c2e3e381037b74c23c0bed6865831d94cef342b669bc3546880e93a3a693b638"
|
||||
stream += "62e587d125fcddd3d84c243e3cac12543b53345a29f14a9e0c7bd5ab59cc9622192224b626df25dff1b348505435d3c7e6321c98542f71deca0620c4"
|
||||
stream += "7790dce4a64b0e7366a1f27382f1465695047d51631d109125c18e85d78cbe7a892318b7e83f8290b80694fd48d264dd4182aab1d1ee0a26934c7c5e"
|
||||
stream += "ad35d8121aee52ae734ab1cd895368982c6baaa76ec024d53a5b0cf05ba3a1058a3fd50ae9f22d021009bddf9b244da70a14fee233b4525c26586513"
|
||||
stream += "5559465827799b3fd280d965202034e0ce1f5bad6eb26182492f76059a103a137281baec82e2c07cbbf238164888a80e181c39953b4cf47c5315f72e"
|
||||
stream += "1d322a39933dff08fd51a2c7c79f489265b7bbedc5fe03d43d7b99fabce55fe8016117c6b64d20f16c014932a2bdaaaf749e3ef9299bbd6a220e6498"
|
||||
stream += "4182220218b6d84685db558c0c58ed114a4f3efd2a4d244db4b2382c3a0448cc0ce622d79fe57293ff2ae65fa52384b7596b23f4044935782e471218"
|
||||
stream += "6ab0dc8ea367129e091367845166597e3f86ecd5b823dd1a2475d72e84d95f78101d5f4d1cac9efe31a41138b181856514c4440aaaf0f9595eb01696"
|
||||
stream += "9670dc3afeca319fd5429b08a9d200203b361d81578fe27e78fb94b87dd88e893163124fb2ded4d5d20128df985b16fcf63bee8d3670534d67a282ea"
|
||||
stream += "c9a13fd906b4370ed2ebd81320c7cdf3c7ec62967696cb859927517dd3c8fcdd51a9f2276494d76beccd469f00034ab4f25b5e84d7c6cb36b23f51ba"
|
||||
stream += "7958294f14fe9f8188d4a90ba3fccf0b40950c73ab12e38561830e522c461f8d37e61ada7d2f89d3c9bd9d89f3bc86736db111f5cefc97e64073fdf2"
|
||||
stream += "8af9967033c7dc8598c09c42afd7fae6a83cf54acb989853c3ee447501eb5739c9e1be3d81d7f9594fffa1166e44fab0c37418fc7102a42cbc56cc00"
|
||||
stream += "e53eb68d3579a0258b65c6918d43340b4b6cd9b4a810a55f97521a7d2a6125596ab309c4e02ba5cb0d0e75fc153fad7fe325a4eb270d0884b0d2f50d"
|
||||
stream += "45cd54fb4026af2e538c5b8732f7a9d5dc5f763cf0d1f33dea2f9e822e0bf90745b0992d6667822390eef8826ce01ab1a06fdccf093c937dccea66ed"
|
||||
stream += "2ec32e6dff20c9e3cb231bdaa3c57de6cf2777e6ed5e6c396db3327b58588c97d4e4ea4fd6980e69e7366dad233947a5952c6159e3e1de101d821d72"
|
||||
stream += "002c427f9de3ebee1abb65ee4154ae4efba21c93c28b2b763722325f63ea64eb4cb52c58943b85001adad44f9810ae04fe6e0695d62fbfc013bfcb00"
|
||||
stream += "ce057d97363acb0a48eaad48cf7d1a1801125eb6a042b25765a828f025e5ff788d7bfdbebf849bcb7bd5fade1497a72986c7b9f11548185a5c1effa9"
|
||||
stream += "afee048920e0173ee5211877ea410ece0254c51b6a1913041250c84771d966d07198a6654cd47be644c5d24785267b62fcb58a7e7849faca56fc08a8"
|
||||
stream += "a76e01f76e0ecfc7c2ae507b54f39c8a6389227b37fe005838dee1b1f64d41a7dc1b2a6ae34bec0fca5fd98e04566e5192b99d4a1bda0040328509be"
|
||||
stream += "b9150abbe72c9da0a0753eddad11e2f8a9b823930fb41082d6ef4198c392e54c59b3e11211f10867870026dabdf9265e2e98e667bfc6c718907de25b"
|
||||
stream += "72ce2aa9360fbc4fdab77de070a286976be4c9563edd9b7b8744032ce454e1e24569d2f778cdad6de4db49acdf6becd9aaf97dc439355d0cb79bbf64"
|
||||
stream += "5ac5d93fc8952070d7d9a1dd9a0b7cde0cf58283ea0d155d88996ddd4a89c1ff6a4901bf3335665b2090e1e8857f59b52f94c0c0af49d8605587ce23"
|
||||
stream += "77007071d78bd2da3d6f500b007c006d785eda6060d061d72ca2fbbfab7ec5e76ebf1473a42d66ad36df15afcc575f42e3cf061a380b9609cd7ef1b1"
|
||||
stream += "f3bc06c5b76a49082020bde388f87b6d49a677ead17611e33de4fa0476d367df03d647ba1c84c3678b5d59aa63fe6319e5998ab7b5771db480d48d7d"
|
||||
stream += "1645ca0979183b38966f9dd6ff9f2cc323f1d78ccf5705e460ccbeba70da1917b2b5a6132a548d0cfc254159733f8666bc4beddddb0eb2dc9bc6e497"
|
||||
stream += "30bf861c8c7370d156cc9789f2547bcf2d15c0429aa20e98769424ac08388ae7db582586626c71e2085f02375b25a41a7a96576ef222a53003a1e3ae"
|
||||
stream += "a24fe80c99b5e42ce826b788c806049f0d67fdff86622a2e79f2a3adde81b3e8c57b9dbaecc682d3caaaa68df97cc5e59fa3b72d64b24f4252e3fc3a"
|
||||
stream += "a7d1adb481c397a38400379b4ccb225cbaba2953a5dca1b1cb786b01dba7ba6f1cd70c527a6cc38de538a5694013951b11c4625d7a35298a84cbb644"
|
||||
stream += "5362a93173938c6960524e6a969912d2315f20a069daa75851538a62c6c5867385b08f4418c024a5d17f69444ef9fa4a34994126356e2dcdf491a58a"
|
||||
stream += "8c72418a15906c5c88a88cf6454fe5fd1d405229749384f8e3c10d3786edb29746bc087c956d01c63e2b13b4bf280959218be9b72e3a78589378086b"
|
||||
stream += "8e06420f6420a2090f56f40c29839eb8925b4d7580fd912601caf750d5e05eb73820153a96cfdd6c771359e1528a81561021677b0046f521403218a3"
|
||||
stream += "d51f917de2ea57952d610d8e8ece093385cd84e4aa1d4c0b21df558055f9b46b59e062f538bc54475e96cb7530a99f2533ec9a50fabc9b3636e7d50e"
|
||||
stream += "940380a31521fdd6643fa56ae8d4359580696d80291b07a210f582b107224675eef361af58ede40b2c040d381e088919a53b137c1d813fd0e6561671"
|
||||
stream += "27d2a097101396fc9b8c8f692792e1004f84a1f5400d2f20474557aad75588e696980c580674fd352ef766e6cb9a8a19e73bede400b625c017f3c630"
|
||||
stream += "d07ea1c54ca8b87b517942b66e9abba802745dff98cf8a8fb8839dfd1b257260590d330890a557d797a33f14374aa916fa7b5db4bc205de64184abd6"
|
||||
stream += "6533e5cea0b5e1141ef8bfb380f4267b50752dac5f8753209676586207891da31ae218362f91ed74cc5771ba5f2b84fe0e69d7c958bf58178ec0e047"
|
||||
stream += "b8cba4d1d39b8be156dc7a43717a668c8539f2f5cb640d67c1c1d445696bc4382485e0b5b5d96fd6dfe9261ca9b8e6fe874ee09d33bbee22a16fa13e"
|
||||
stream += "9df931faef02b3da9a25b977c8c01ac785b4c9a5eeb9a59f78c3a976d6a21df9cd8fd122b99defd367f2fc3375f91d8125b2bafeec235b95d5d2a3e7"
|
||||
stream += "a096dd20a36083245c74371b8250346d27e1c115623eea2c66f9a4ec33e6401bde4c68460ab0708866340a15833b04776d4199d71f3c296b5a409ac0"
|
||||
stream += "053c29fe94e799e3a43435cd719d23b10d11e3327cde17cfbcb28da13b7fb7db9f038cd9509a0c9117702be57f16fd93f8f800f3c5ca816491cb0ba3"
|
||||
stream += "49b65206d768a6661f349b7ae3179462b351e0c3e3628f72f02892ae31a2e72bd8db023984f35664ea8fef4bd2f17cd0cec5c4fca7c35efe7552c583"
|
||||
stream += "582781b107152edfd454b006a0f9d0cc39aa77b43c6436be9ec776b4b7d7552be92d664a2271c8d6593c5ac9d99c40b443aba812f02c77a8bf7f51dc"
|
||||
stream += "91e13f9a933a8cca0aeee6af0195c5cda1cd2a806e52aa944cc72b938352da2f524c6a3504f355a88a142ab50fdfefa3803322acc86d6f66c6120c68"
|
||||
stream += "ad14b2f0940b8b79ea3b5231c631e6186bb07c4ef93fbcd0237ab06aa069e56db78337458f767166fa5de89b5475ad75e06485c02f103404a404619b"
|
||||
stream += "c6f38dbc0050fcf553e153aa7e19367df394437dcbd91c37d8020587530d1a45b9a5ed76a66491d5b308a185d556e3abb06c8744888a8d1ac57ac490"
|
||||
stream += "5ee6b076fcf552f33d3efd32b8857dbaea42397e6cbf2c706d6d674f52fc8ea193a093008d7c7dab0440b97ac95223a3fd80d0ddb1eecdbfc3046545"
|
||||
stream += "2da89c129f15d9a165b83ba4edf41064d4a3006adb4167158cebc5e94c96a1cd20818bbddfcce2d62f67e36e1c6b9150ea01ad2c834257ce01dec049"
|
||||
stream += "b2bc5d1528feb7400a353b49926944cebb1286fd3a7829c76a948e45d50119bfb8fcdfda8380ad7bb4950b6fe5fccbb50939d6fd801a00e157a693d0"
|
||||
stream += "4938dc498538c6f9a56157ff4cf1efa517fa173904d16b9b5bb3289127fbdfa30a4a66d340c87f04b53c1ce407dd0d8f3a63b8ce9bb211881b5b9808"
|
||||
stream += "6c6848cfb9635bddeebf6bafc74771f7b2681f3ec6d32fc31a1bd9c8284714d88ccd2a4a8cf6961f0c7c83e115ad982606cf42707202ba0860e83fbf"
|
||||
stream += "9a9a7b594d1a60ccc6bf6b637ab95e461decac0810b7eea88a9e25c8cfb2b9d2e0a83802a70bab37b627ff7f98375a260a5ac2cb0637294956874c28"
|
||||
stream += "3b97cab4616c8e297a7652bb8043c891e5242a7fff99becf6ee8a8090b8f96b7667b666679d6f25f5a52c42ce02100cd4cd2dd7a1e1ce493fdb41f59"
|
||||
stream += "fa18f4e35fd6cd74f3b005ca852afa3cf086cc0b90291711e6fc0c186231dce1be644a599435b8872f7ab8c63a38d92db5cc944ecc7b3a96dc603bc8"
|
||||
stream += "7fd09edac53f19104b74b215178045053dc4e1d1e919e44e920f49f4e73417be50a9ef64d54f65feea6207c7caaaaa8feb9608fe8de3c473d134a092"
|
||||
stream += "42f52f146eea15844e7652aa5036c7254fd2006d8b57074cf5ac2f8a916592b6ad926910b22ade6e7797ac5be196d10bb5851719c8dc547657180647"
|
||||
stream += "ff701df8f9c574b29715de17c16f57d26a084f83bbff21b72bc67af4fc505f7879cfe4b4132952eef7e6a111425b20507e45f4478ce99882db0235b3"
|
||||
stream += "b68b7bc0ec7f2a20e9291c82aec433d704bef5cb71406ac9172988df7aacba40eb257c2c4e2fddfab8e9a8ef884716a26e96848d9819841ab0855c2b"
|
||||
stream += "76aa319c170637afa8b0c86e2b3493b675b4b83ab684205c67e1abce31e8e486a8406493251921ebe834e32a49874240289c31563c39be5fa7d73e3c"
|
||||
stream += "6dc4060943e4714f477bf785c2e8bc73a9c6cfd12210e7d4c13deee8cb92f3ccff3131af4913b7edb3dc5ea5e5969dce60522dc5bfc1b250cf53550b"
|
||||
stream += "4cd929b10a37d91f8bc9dfcd43fdfff1e72689aa415204924b1d73a047a92f69e02f286ba928fba152347ee9e459e2cb51eef3202c5281c7c8dd53f8"
|
||||
stream += "507e34f9edc0eb86dfa258f4ae6971bd20da0ba1b3f9223dada9a8e05634153611475a11ce2b670ba4d01dd8782925748c31850659d23d67d28884a2"
|
||||
stream += "c854404a04dba8a41a8c98e06f2c0a280427e1beeaae44fad98b077d900b6d9f55ddc59ce9543f5c4b7be31e3e3491e485ace8047e65cf5b51684f87"
|
||||
stream += "dad986729e7a6b2f94703109713dbd77454ed950882835d705baf9997d59ead5b0adbaa734f2539e990ae0672eb83a290ad95e6f1a4330a0f6bcb7b3"
|
||||
stream += "41d4429c223091427adaaf48c69c8c59011d7dad040a2ce5816085dbca8a532ae267c77565665044e1c905a8617b00f8e7d3ce393845f51fd58ae160"
|
||||
stream += "43c72f82f5e2bf2486b6ceab75fc4bd5e02f217fae2cb80c035d7289af39537699a844e9e4807fb01df4a2c87aacaeba9fbc02735a9ec6c013f907e2"
|
||||
stream += "9e6c158615eab92c7cc12265a7dc0f060421fb4ca70dafc80dce47daa42af4432248f995c2f446e31efdbef26aeb6eaae9442275aba397364a83882f"
|
||||
stream += "f45a05d11391f4499a22783bca612dc61c9812063c24a8b9b2cbe16af31931f63091888eb8bca0b71861a92000ffb72bce0206fd730cd35267f74d7d"
|
||||
stream += "8d449ef5dc1c5100df72b0c921c48c32a91c0b7b409a07103eae14991e15f870252af33fb95a3147c75871ff7de2e3ef229d95fde60fb17960d1af8c"
|
||||
stream += "975c80646f09326e7f1bf7ad4746c0a97abf813936b782320f32dad425cbd6299788ef7bbe81ce9ddbf07833949c5380e932d73509dba78efd8682d5"
|
||||
stream += "066437e36d0e2febd7d5c0fd47596f5ad0db378af06f456317965168a4cf605de5c94f65107fe2c32d5cbb3237b6651923056ba699fb757df6da86d7"
|
||||
stream += "79a899f668ab9ce6ca5a64f85bfe2a00153403d30c543a7ea68d1c475945a4b138b9d68ba0829d736314c965845e9022738c65d20388f21a7bbc1077"
|
||||
stream += "da441d9927b354298d183517d7e0c7304bdf14259cf56837b23fd9c78dad0d47efd021c29be31b360faa0b1cc00e96bd3cc015a92fff2d8a4b86c133"
|
||||
stream += "57fd9b5179402e69f19736401f7a3ca75cecd142eb2dcf4f9fae4d5291fc9cbd1d59ea355eeb6a761092f31b013eb31ad4bd35775b48a913b336f0ab"
|
||||
stream += "83105461586fa1a3caf93ca9141659eee18974674a68735e5e27d3bd6aaeb3e0a66d5998d95f125d33a53baf84827a689e9fdec36cf9445af99f99eb"
|
||||
stream += "a692f7dbcf1b58821e31469cc94ce0b6ce372492fc1efb6578e528f642fd5988dd206f11022683bdbd8073b79b77b34cddce81496bbfeaf627078f25"
|
||||
stream += "b3573dfe5e37f40d251d8af2f1cf321dba17a8373642d71db1ed29db5941df01ebaddfa157a2b750b98b647af2bf785e5cc83b8088728e8a560d2b08"
|
||||
stream += "51e57e5adc719b087c082360570a9efbfa4715651078141bab8b58acb0bec9087af6db2a4abf23d800d743c8cade4f02d949742c00d2b22cfad8d977"
|
||||
stream += "c211d5cc5d2b5c75e17210f76ab711513ad6b7e905329a76b7cc6acc5f9861be20132ffb4e0da5fc568312ad2d40edc7489fa3a190a5559e12df5afb"
|
||||
stream += "362fd3b67dab874314a804b0a510cf64cf64f479af71c106a208f05d89e21308190795deb9b179fcee275f5d77c1f0b4e1844a0d1fd0de370ee49351"
|
||||
stream += "171145dbb88491fc9f5443cd2b8065b846349fc869bf7fbada55988f57cb51c534a226f93a3c5e58b5478f073c9bb3444b81189df6346b06d8c7d380"
|
||||
stream += "1bdc417c89c3f037b2ca60469d09110865f35aba261f7f3f0241b4fd252146ddb80e416f131269e9dcf4d134a11c1c0e0f136f04bf4657204ad349bb"
|
||||
stream += "b33442d99fe9560d1558cc20936877ef77d62771df37116ffb6c31c872de5952a19d530703a98133629a1aeeffc37b60d0587293dd43e43bcf175d57"
|
||||
stream += "09a0879454b7c7e80ebfbd3f030b2420deb0b664f8900f1939a7f611b01ad92d539d448f4a1f7cba8f9738f353fa038258666d73c4fdbff3dc122321"
|
||||
stream += "9fd3dd4820baddc7aa9e3434745938d9aa2dee71e119228ba6eef0ac014eedad9033bc28741d29a7c98c68441143fba8a8016a13068529dd135d01f9"
|
||||
stream += "1dd8ea549ea2af7aaff01c602f226a2bb29e46b8b191640bbbe4e5d31c3a04fe2d2c44992a2d68279e346d7385eac6d59eec08c2b2bf5adaf17ab42f"
|
||||
stream += "55f06eb10581d7a8b730768a784f0bbcb94222f1155f912e6424fbb51c80e833e8d8f78d2229a8fb9e51dc05dc90659e1e5b22993e12f7bbf28cd948"
|
||||
stream += "3c41ca5930763d25bf9b0d7c211bdb65310c9f617fad30b03adf303fa4a853052d5ae077f9be6ad53fe29bf6ae43b97eea9d9524b2a6bb32572f38de"
|
||||
stream += "c080ff67a537153b373383f33056e1a59f0730dcbd99d60b2165b4c043673e747bd885d6177fd4310dde0fb0ab8ace560273cf20ccebf04dcba0f89e"
|
||||
stream += "1d24e5d1f0755fea07800de84475bcdba38078a6a04b8def1b21be0c4c80e92aa80399e4d2223eadf0458e8a1edf12745618aa6c20f0d429e7000e94"
|
||||
stream += "c06d95376ca92ac13d9b9564c5859e86a544cad717156aa256815dd116f34c542c60804c34311ed582f6febaf633e80c7ce8f5fa34a82ac7aaa0d993"
|
||||
stream += "c9b67ec2a44ee6dfad48fc8db1e8afb861fe750dcb324a09648d75cb527e1bd17a9bc1d60cce2b28b635bbdaa52c2fba2a5ac18737a59643bd2f32b2"
|
||||
stream += "39fbbc8ada2ffa9d6ac10276274a5823e3a866c0d98c2c0dc5334bc5102fc91bea7001d124063634d9f22955094b82f6cf82ff22a2679dbbc4307eb8"
|
||||
stream += "2d96fd6920801476ecfbf412a024445d080c1b3079f60ae484fd8214516c2dde011f5c0882f60e2c753a01f3e761ac32a5981beae530e851f4aff9bb"
|
||||
stream += "a328e655ebc76b2c6a92c25bfd8141f762abb0d877df5563a0300e42897aee78a2c0ec05d8faa5f633148534246d12eec5a488ceb16495faec22137f"
|
||||
stream += "7abcfe19eeac83af668b54c9ca11ff3fe6bc35d7817c620864cbfcff0027075f93185197a08e3a0b8efaf70859dfa08cc3d1c7c9e7c8ee95ea469649"
|
||||
stream += "4443f318cf7475fc28cb63feeb58130fd5296a56bfcce0ae96ad1c0723edcfc08c6f44176928eca3f5dfd9610e4de5e4d129b554a5c526d38e4088ec"
|
||||
stream += "0c48ec037ed05356f2687f7a33551c3ac15772cea4ebc764549f0a18e6fc6236bfc481662968caea5435334bcf4c27c7369e0f59c0729c48538611fa"
|
||||
stream += "2594e0ad63b410d39a36760fe5dfc0105e2a87b51ffcea4eb70ef55969e59fb7ba41700d93cb590ca73335a00a333515819d9d4740b25b933f4ba4ce"
|
||||
stream += "8a65f0365072a88e478330dcd751028fc5a084ee0c01568a47905473fc5346bab0eb7c097bc73f8119ddfbff755f8b2fa1fe28982a84ffdcf921a7be"
|
||||
stream += "98ffc2a7e1cb27ad9ba45a3aaa6ee19a9ef5f1df65459df4b27eaffa87d7cef2"
|
||||
stream = unhexlify(stream)
|
||||
|
||||
+135
-94
@@ -1,5 +1,5 @@
|
||||
|
||||
from . import cgcrandom
|
||||
|
||||
# TODO use cpu factory
|
||||
from ..native.cpu.x86 import I386Cpu
|
||||
from ..native.cpu.abstractcpu import Interruption, ConcretizeRegister, ConcretizeArgument
|
||||
@@ -24,8 +24,8 @@ class Deadlock(Exception):
|
||||
|
||||
|
||||
class SymbolicSyscallArgument(ConcretizeRegister):
|
||||
def __init__(self, cpu, number, message='Concretizing syscall argument', policy='SAMPLED'):
|
||||
reg_name = ['EBX', 'ECX', 'EDX', 'ESI', 'EDI', 'EBP'][number]
|
||||
def __init__(self, cpu, number, message="Concretizing syscall argument", policy="SAMPLED"):
|
||||
reg_name = ["EBX", "ECX", "EDX", "ESI", "EDI", "EBP"][number]
|
||||
super().__init__(cpu, reg_name, message, policy)
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ class Decree(Platform):
|
||||
A simple Decree Operating System.
|
||||
This class emulates the most common Decree system calls
|
||||
"""
|
||||
|
||||
CGC_EBADF = 1
|
||||
CGC_EFAULT = 2
|
||||
CGC_EINVAL = 3
|
||||
@@ -169,17 +170,17 @@ class Decree(Platform):
|
||||
|
||||
def __getstate__(self):
|
||||
state = super().__getstate__()
|
||||
state['clocks'] = self.clocks
|
||||
state['input'] = self.input.buffer
|
||||
state['output'] = self.output.buffer
|
||||
state['files'] = [x.buffer for x in self.files]
|
||||
state['procs'] = self.procs
|
||||
state['current'] = self._current
|
||||
state['running'] = self.running
|
||||
state['rwait'] = self.rwait
|
||||
state['twait'] = self.twait
|
||||
state['timers'] = self.timers
|
||||
state['syscall_trace'] = self.syscall_trace
|
||||
state["clocks"] = self.clocks
|
||||
state["input"] = self.input.buffer
|
||||
state["output"] = self.output.buffer
|
||||
state["files"] = [x.buffer for x in self.files]
|
||||
state["procs"] = self.procs
|
||||
state["current"] = self._current
|
||||
state["running"] = self.running
|
||||
state["rwait"] = self.rwait
|
||||
state["twait"] = self.twait
|
||||
state["timers"] = self.timers
|
||||
state["syscall_trace"] = self.syscall_trace
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
@@ -189,12 +190,12 @@ class Decree(Platform):
|
||||
"""
|
||||
super().__setstate__(state)
|
||||
self.input = Socket()
|
||||
self.input.buffer = state['input']
|
||||
self.input.buffer = state["input"]
|
||||
self.output = Socket()
|
||||
self.output.buffer = state['output']
|
||||
self.output.buffer = state["output"]
|
||||
|
||||
self.files = []
|
||||
for buf in state['files']:
|
||||
for buf in state["files"]:
|
||||
f = Socket()
|
||||
f.buffer = buf
|
||||
self.files.append(f)
|
||||
@@ -207,15 +208,15 @@ class Decree(Platform):
|
||||
self.files[2].peer = self.output
|
||||
self.input.peer = self.files[0]
|
||||
|
||||
self.procs = state['procs']
|
||||
self._current = state['current']
|
||||
self.running = state['running']
|
||||
self.rwait = state['rwait']
|
||||
self.twait = state['twait']
|
||||
self.timers = state['timers']
|
||||
self.clocks = state['clocks']
|
||||
self.procs = state["procs"]
|
||||
self._current = state["current"]
|
||||
self.running = state["running"]
|
||||
self.rwait = state["rwait"]
|
||||
self.twait = state["twait"]
|
||||
self.timers = state["timers"]
|
||||
self.clocks = state["clocks"]
|
||||
|
||||
self.syscall_trace = state['syscall_trace']
|
||||
self.syscall_trace = state["syscall_trace"]
|
||||
|
||||
# Install event forwarders
|
||||
for proc in self.procs:
|
||||
@@ -229,7 +230,7 @@ class Decree(Platform):
|
||||
filename = ""
|
||||
for i in range(0, 1024):
|
||||
c = Operators.CHR(cpu.read_int(buf + i, 8))
|
||||
if c == '\x00':
|
||||
if c == "\x00":
|
||||
break
|
||||
filename += c
|
||||
return filename
|
||||
@@ -246,16 +247,16 @@ class Decree(Platform):
|
||||
TASK_SIZE = 0x80000000
|
||||
|
||||
def CGC_PAGESTART(_v):
|
||||
return ((_v) & ~ (CGC_MIN_ALIGN - 1))
|
||||
return (_v) & ~(CGC_MIN_ALIGN - 1)
|
||||
|
||||
def CGC_PAGEOFFSET(_v):
|
||||
return ((_v) & (CGC_MIN_ALIGN - 1))
|
||||
return (_v) & (CGC_MIN_ALIGN - 1)
|
||||
|
||||
def CGC_PAGEALIGN(_v):
|
||||
return (((_v) + CGC_MIN_ALIGN - 1) & ~(CGC_MIN_ALIGN - 1))
|
||||
return ((_v) + CGC_MIN_ALIGN - 1) & ~(CGC_MIN_ALIGN - 1)
|
||||
|
||||
def BAD_ADDR(x):
|
||||
return ((x) >= TASK_SIZE)
|
||||
return (x) >= TASK_SIZE
|
||||
|
||||
# load elf See https://github.com/CyberdyneNYC/linux-source-3.13.2-cgc/blob/master/fs/binfmt_cgc.c
|
||||
# read the ELF object file
|
||||
@@ -265,7 +266,7 @@ class Decree(Platform):
|
||||
cpu = self._mk_proc()
|
||||
|
||||
bss = brk = 0
|
||||
start_code = 0xffffffff
|
||||
start_code = 0xFFFFFFFF
|
||||
end_code = start_data = end_data = 0
|
||||
|
||||
for (vaddr, memsz, perms, name, offset, filesz) in cgc.maps():
|
||||
@@ -274,8 +275,12 @@ class Decree(Platform):
|
||||
if start_data < vaddr:
|
||||
start_data = vaddr
|
||||
|
||||
if vaddr > TASK_SIZE or filesz > memsz or \
|
||||
memsz > TASK_SIZE or TASK_SIZE - memsz < vaddr:
|
||||
if (
|
||||
vaddr > TASK_SIZE
|
||||
or filesz > memsz
|
||||
or memsz > TASK_SIZE
|
||||
or TASK_SIZE - memsz < vaddr
|
||||
):
|
||||
raise Exception("Set_brk can never work. avoid overflows")
|
||||
|
||||
# CGCMAP--
|
||||
@@ -303,9 +308,9 @@ class Decree(Platform):
|
||||
hi = CGC_PAGEALIGN(vaddr + memsz)
|
||||
if hi - lo > 0:
|
||||
old_perms = cpu.memory.perms(lo)
|
||||
cpu.memory.mprotect(lo, hi - lo, 'rw')
|
||||
cpu.memory.mprotect(lo, hi - lo, "rw")
|
||||
try:
|
||||
cpu.memory[lo:hi] = '\x00' * (hi - lo)
|
||||
cpu.memory[lo:hi] = "\x00" * (hi - lo)
|
||||
except Exception as e:
|
||||
logger.debug("Exception zeroing main elf fractional pages: %s" % str(e))
|
||||
cpu.memory.mprotect(lo, hi, old_perms)
|
||||
@@ -317,7 +322,7 @@ class Decree(Platform):
|
||||
k = vaddr + filesz
|
||||
if k > bss:
|
||||
bss = k
|
||||
if 'x' in perms and end_code < k:
|
||||
if "x" in perms and end_code < k:
|
||||
end_code = k
|
||||
if end_data < k:
|
||||
end_data = k
|
||||
@@ -327,43 +332,46 @@ class Decree(Platform):
|
||||
brk = k
|
||||
|
||||
bss = brk
|
||||
stack_base = 0xbaaaaffc
|
||||
stack_base = 0xBAAAAFFC
|
||||
stack_size = 0x800000
|
||||
stack = cpu.memory.mmap(0xbaaab000 - stack_size, stack_size, 'rwx') + stack_size - 4
|
||||
stack = cpu.memory.mmap(0xBAAAB000 - stack_size, stack_size, "rwx") + stack_size - 4
|
||||
assert (stack_base) in cpu.memory and (stack_base - stack_size + 4) in cpu.memory
|
||||
|
||||
# Only one thread in Decree
|
||||
status, thread = next(cgc.threads())
|
||||
assert status == 'Running'
|
||||
assert status == "Running"
|
||||
|
||||
logger.info("Setting initial cpu state")
|
||||
cpu.write_register('EAX', 0x0)
|
||||
cpu.write_register('ECX', cpu.memory.mmap(CGC_PAGESTART(0x4347c000),
|
||||
CGC_PAGEALIGN(4096 + CGC_PAGEOFFSET(0x4347c000)),
|
||||
'rwx'))
|
||||
cpu.write_register('EDX', 0x0)
|
||||
cpu.write_register('EBX', 0x0)
|
||||
cpu.write_register('ESP', stack)
|
||||
cpu.write_register('EBP', 0x0)
|
||||
cpu.write_register('ESI', 0x0)
|
||||
cpu.write_register('EDI', 0x0)
|
||||
cpu.write_register('EIP', thread['EIP'])
|
||||
cpu.write_register('RFLAGS', 0x202)
|
||||
cpu.write_register('CS', 0x0)
|
||||
cpu.write_register('SS', 0x0)
|
||||
cpu.write_register('DS', 0x0)
|
||||
cpu.write_register('ES', 0x0)
|
||||
cpu.write_register('FS', 0x0)
|
||||
cpu.write_register('GS', 0x0)
|
||||
cpu.write_register("EAX", 0x0)
|
||||
cpu.write_register(
|
||||
"ECX",
|
||||
cpu.memory.mmap(
|
||||
CGC_PAGESTART(0x4347C000), CGC_PAGEALIGN(4096 + CGC_PAGEOFFSET(0x4347C000)), "rwx"
|
||||
),
|
||||
)
|
||||
cpu.write_register("EDX", 0x0)
|
||||
cpu.write_register("EBX", 0x0)
|
||||
cpu.write_register("ESP", stack)
|
||||
cpu.write_register("EBP", 0x0)
|
||||
cpu.write_register("ESI", 0x0)
|
||||
cpu.write_register("EDI", 0x0)
|
||||
cpu.write_register("EIP", thread["EIP"])
|
||||
cpu.write_register("RFLAGS", 0x202)
|
||||
cpu.write_register("CS", 0x0)
|
||||
cpu.write_register("SS", 0x0)
|
||||
cpu.write_register("DS", 0x0)
|
||||
cpu.write_register("ES", 0x0)
|
||||
cpu.write_register("FS", 0x0)
|
||||
cpu.write_register("GS", 0x0)
|
||||
|
||||
cpu.memory.mmap(0x4347c000, 0x1000, 'r')
|
||||
cpu.memory.mmap(0x4347C000, 0x1000, "r")
|
||||
# cpu.memory[0x4347c000:0x4347d000] = 'A' 0x1000
|
||||
|
||||
logger.info("Entry point: %016x", cpu.EIP)
|
||||
logger.info("Stack start: %016x", cpu.ESP)
|
||||
logger.info("Brk: %016x", brk)
|
||||
logger.info("Mappings:")
|
||||
for m in str(cpu.memory).split('\n'):
|
||||
for m in str(cpu.memory).split("\n"):
|
||||
logger.info(" %s", m)
|
||||
return [cpu]
|
||||
|
||||
@@ -432,7 +440,7 @@ class Decree(Platform):
|
||||
logger.info("ALLOCATE: addr points to invalid address. Returning EFAULT")
|
||||
return Decree.CGC_EFAULT
|
||||
|
||||
perms = ['rw ', 'rwx'][bool(isX)]
|
||||
perms = ["rw ", "rwx"][bool(isX)]
|
||||
try:
|
||||
result = cpu.memory.mmap(None, length, perms)
|
||||
except Exception as e:
|
||||
@@ -483,7 +491,9 @@ class Decree(Platform):
|
||||
return Decree.CGC_EFAULT
|
||||
cpu.write_int(rnd_bytes, len(data), 32)
|
||||
|
||||
logger.info("RANDOM(0x%08x, %d, 0x%08x) -> <%s>)" % (buf, count, rnd_bytes, repr(data[:10])))
|
||||
logger.info(
|
||||
"RANDOM(0x%08x, %d, 0x%08x) -> <%s>)" % (buf, count, rnd_bytes, repr(data[:10]))
|
||||
)
|
||||
return ret
|
||||
|
||||
def sys_receive(self, cpu, fd, buf, count, rx_bytes):
|
||||
@@ -502,7 +512,7 @@ class Decree(Platform):
|
||||
EBADF fd is not a valid file descriptor or is not open
|
||||
EFAULT buf or rx_bytes points to an invalid address.
|
||||
"""
|
||||
data = ''
|
||||
data = ""
|
||||
if count != 0:
|
||||
if not self._is_open(fd):
|
||||
logger.info("RECEIVE: Not valid file descriptor on receive. Returning EBADF")
|
||||
@@ -513,8 +523,8 @@ class Decree(Platform):
|
||||
logger.info("RECEIVE: buf points to invalid address. Returning EFAULT")
|
||||
return Decree.CGC_EFAULT
|
||||
|
||||
#import random
|
||||
#count = random.randint(1,count)
|
||||
# import random
|
||||
# count = random.randint(1,count)
|
||||
if fd > 2 and self.files[fd].is_empty():
|
||||
cpu.PC -= cpu.instruction.size
|
||||
self.wait([fd], [], None)
|
||||
@@ -538,7 +548,10 @@ class Decree(Platform):
|
||||
return Decree.CGC_EFAULT
|
||||
cpu.write_int(rx_bytes, len(data), 32)
|
||||
|
||||
logger.info("RECEIVE(%d, 0x%08x, %d, 0x%08x) -> <%s> (size:%d)" % (fd, buf, count, rx_bytes, repr(data)[:min(count, 10)], len(data)))
|
||||
logger.info(
|
||||
"RECEIVE(%d, 0x%08x, %d, 0x%08x) -> <%s> (size:%d)"
|
||||
% (fd, buf, count, rx_bytes, repr(data)[: min(count, 10)], len(data))
|
||||
)
|
||||
return 0
|
||||
|
||||
def sys_transmit(self, cpu, fd, buf, count, tx_bytes):
|
||||
@@ -577,11 +590,14 @@ class Decree(Platform):
|
||||
value = Operators.CHR(cpu.read_int(buf + i, 8))
|
||||
if not isinstance(value, str):
|
||||
logger.debug("TRANSMIT: Writing symbolic values to file %d", fd)
|
||||
#value = str(value)
|
||||
# value = str(value)
|
||||
data.append(value)
|
||||
self.files[fd].transmit(data)
|
||||
|
||||
logger.info("TRANSMIT(%d, 0x%08x, %d, 0x%08x) -> <%.24r>" % (fd, buf, count, tx_bytes, ''.join([str(x) for x in data])))
|
||||
logger.info(
|
||||
"TRANSMIT(%d, 0x%08x, %d, 0x%08x) -> <%.24r>"
|
||||
% (fd, buf, count, tx_bytes, "".join([str(x) for x in data]))
|
||||
)
|
||||
self.syscall_trace.append(("_transmit", fd, data))
|
||||
self.signal_transmit(fd)
|
||||
|
||||
@@ -605,11 +621,15 @@ class Decree(Platform):
|
||||
self.running.remove(procid)
|
||||
# self.procs[procid] = None #let it there so we can report?
|
||||
if issymbolic(error_code):
|
||||
logger.info("TERMINATE PROC_%02d with symbolic exit code [%d,%d]", procid, solver.minmax(self.constraints, error_code))
|
||||
logger.info(
|
||||
"TERMINATE PROC_%02d with symbolic exit code [%d,%d]",
|
||||
procid,
|
||||
solver.minmax(self.constraints, error_code),
|
||||
)
|
||||
else:
|
||||
logger.info("TERMINATE PROC_%02d %x", procid, error_code)
|
||||
if len(self.running) == 0:
|
||||
raise TerminateState(f'Process exited correctly. Code: {error_code}')
|
||||
raise TerminateState(f"Process exited correctly. Code: {error_code}")
|
||||
return error_code
|
||||
|
||||
def sys_deallocate(self, cpu, addr, size):
|
||||
@@ -641,7 +661,7 @@ class Decree(Platform):
|
||||
"""
|
||||
logger.info("DEALLOCATE(0x%08x, %d)" % (addr, size))
|
||||
|
||||
if addr & 0xfff != 0:
|
||||
if addr & 0xFFF != 0:
|
||||
logger.info("DEALLOCATE: addr is not page aligned")
|
||||
return Decree.CGC_EINVAL
|
||||
if size == 0:
|
||||
@@ -659,7 +679,10 @@ class Decree(Platform):
|
||||
def sys_fdwait(self, cpu, nfds, readfds, writefds, timeout, readyfds):
|
||||
""" fdwait - wait for file descriptors to become ready
|
||||
"""
|
||||
logger.debug("FDWAIT(%d, 0x%08x, 0x%08x, 0x%08x, 0x%08x)" % (nfds, readfds, writefds, timeout, readyfds))
|
||||
logger.debug(
|
||||
"FDWAIT(%d, 0x%08x, 0x%08x, 0x%08x, 0x%08x)"
|
||||
% (nfds, readfds, writefds, timeout, readyfds)
|
||||
)
|
||||
|
||||
if timeout:
|
||||
if timeout not in cpu.memory: # todo: size
|
||||
@@ -682,7 +705,7 @@ class Decree(Platform):
|
||||
bits = cpu.read_int(writefds, fds_bitsize)
|
||||
|
||||
for fd in range(nfds):
|
||||
if (bits & 1 << fd):
|
||||
if bits & 1 << fd:
|
||||
if self.files[fd].is_full():
|
||||
writefds_wait.add(fd)
|
||||
else:
|
||||
@@ -696,7 +719,7 @@ class Decree(Platform):
|
||||
return Decree.CGC_EFAULT
|
||||
bits = cpu.read_int(readfds, fds_bitsize)
|
||||
for fd in range(nfds):
|
||||
if (bits & 1 << fd):
|
||||
if bits & 1 << fd:
|
||||
if self.files[fd].is_empty():
|
||||
readfds_wait.add(fd)
|
||||
else:
|
||||
@@ -707,14 +730,21 @@ class Decree(Platform):
|
||||
if timeout != 0:
|
||||
seconds = cpu.read_int(timeout, 32)
|
||||
microseconds = cpu.read_int(timeout + 4, 32)
|
||||
logger.info("FDWAIT: waiting for read on fds: {%s} and write to: {%s} timeout: %d", repr(
|
||||
list(readfds_wait)), repr(list(writefds_wait)), microseconds + 1000 * seconds)
|
||||
logger.info(
|
||||
"FDWAIT: waiting for read on fds: {%s} and write to: {%s} timeout: %d",
|
||||
repr(list(readfds_wait)),
|
||||
repr(list(writefds_wait)),
|
||||
microseconds + 1000 * seconds,
|
||||
)
|
||||
to = microseconds + 1000 * seconds
|
||||
# no ready file, wait
|
||||
else:
|
||||
to = None
|
||||
logger.info("FDWAIT: waiting for read on fds: {%s} and write to: {%s} timeout: INDIFENITELY",
|
||||
repr(list(readfds_wait)), repr(list(writefds_wait)))
|
||||
logger.info(
|
||||
"FDWAIT: waiting for read on fds: {%s} and write to: {%s} timeout: INDIFENITELY",
|
||||
repr(list(readfds_wait)),
|
||||
repr(list(writefds_wait)),
|
||||
)
|
||||
|
||||
cpu.PC -= cpu.instruction.size
|
||||
self.wait(readfds_wait, writefds_wait, to)
|
||||
@@ -726,13 +756,13 @@ class Decree(Platform):
|
||||
for fd in readfds_ready:
|
||||
bits |= 1 << fd
|
||||
for byte in range(0, nfds, 8):
|
||||
cpu.write_int(readfds, (bits >> byte) & 0xff, 8)
|
||||
cpu.write_int(readfds, (bits >> byte) & 0xFF, 8)
|
||||
if writefds:
|
||||
bits = 0
|
||||
for fd in writefds_ready:
|
||||
bits |= 1 << fd
|
||||
for byte in range(0, nfds, 8):
|
||||
cpu.write_int(writefds, (bits >> byte) & 0xff, 8)
|
||||
cpu.write_int(writefds, (bits >> byte) & 0xFF, 8)
|
||||
|
||||
logger.info("FDWAIT: continuing. Some file is ready Readyfds: %08x", readyfds)
|
||||
if readyfds:
|
||||
@@ -747,21 +777,22 @@ class Decree(Platform):
|
||||
:param cpu: current CPU.
|
||||
_terminate, transmit, receive, fdwait, allocate, deallocate and random
|
||||
"""
|
||||
syscalls = {0x00000001: self.sys_terminate,
|
||||
0x00000002: self.sys_transmit,
|
||||
0x00000003: self.sys_receive,
|
||||
0x00000004: self.sys_fdwait,
|
||||
0x00000005: self.sys_allocate,
|
||||
0x00000006: self.sys_deallocate,
|
||||
0x00000007: self.sys_random,
|
||||
}
|
||||
syscalls = {
|
||||
0x00000001: self.sys_terminate,
|
||||
0x00000002: self.sys_transmit,
|
||||
0x00000003: self.sys_receive,
|
||||
0x00000004: self.sys_fdwait,
|
||||
0x00000005: self.sys_allocate,
|
||||
0x00000006: self.sys_deallocate,
|
||||
0x00000007: self.sys_random,
|
||||
}
|
||||
if cpu.EAX not in syscalls.keys():
|
||||
raise TerminateState(f"32 bit DECREE system call number {cpu.EAX} Not Implemented")
|
||||
func = syscalls[cpu.EAX]
|
||||
logger.debug("SYSCALL32: %s (nargs: %d)", func.__name__, func.__code__.co_argcount)
|
||||
nargs = func.__code__.co_argcount
|
||||
args = [cpu, cpu.EBX, cpu.ECX, cpu.EDX, cpu.ESI, cpu.EDI, cpu.EBP]
|
||||
cpu.EAX = func(*args[:nargs - 1])
|
||||
cpu.EAX = func(*args[: nargs - 1])
|
||||
|
||||
def sched(self):
|
||||
""" Yield CPU.
|
||||
@@ -800,7 +831,13 @@ class Decree(Platform):
|
||||
yields the cpu to another running process.
|
||||
"""
|
||||
logger.info("WAIT:")
|
||||
logger.info("\tProcess %d is going to wait for [ %r %r %r ]", self._current, readfds, writefds, timeout)
|
||||
logger.info(
|
||||
"\tProcess %d is going to wait for [ %r %r %r ]",
|
||||
self._current,
|
||||
readfds,
|
||||
writefds,
|
||||
timeout,
|
||||
)
|
||||
logger.info("\tProcess: %r", self.procs)
|
||||
logger.info("\tRunning: %r", self.running)
|
||||
logger.info("\tRWait: %r", self.rwait)
|
||||
@@ -831,7 +868,9 @@ class Decree(Platform):
|
||||
|
||||
def awake(self, procid):
|
||||
""" Remove procid from waitlists and reestablish it in the running list """
|
||||
logger.info("Remove procid:%d from waitlists and reestablish it in the running list", procid)
|
||||
logger.info(
|
||||
"Remove procid:%d from waitlists and reestablish it in the running list", procid
|
||||
)
|
||||
for wait_list in self.rwait:
|
||||
if procid in wait_list:
|
||||
wait_list.remove(procid)
|
||||
@@ -902,6 +941,7 @@ class Decree(Platform):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
############################################################################
|
||||
# Symbolic versions follows
|
||||
|
||||
@@ -938,13 +978,13 @@ class SDecree(Decree):
|
||||
# marshaling/pickle
|
||||
def __getstate__(self):
|
||||
state = super().__getstate__()
|
||||
state['constraints'] = self.constraints
|
||||
state['random'] = self.random
|
||||
state["constraints"] = self.constraints
|
||||
state["random"] = self.random
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
self._constraints = state['constraints']
|
||||
self.random = state['random']
|
||||
self._constraints = state["constraints"]
|
||||
self.random = state["random"]
|
||||
super().__setstate__(state)
|
||||
|
||||
def sys_receive(self, cpu, fd, buf, count, rx_bytes):
|
||||
@@ -1071,6 +1111,7 @@ class DecreeEmu:
|
||||
@staticmethod
|
||||
def cgc_random(platform, buf, count, rnd_bytes):
|
||||
from . import cgcrandom
|
||||
|
||||
if issymbolic(buf):
|
||||
logger.info("Ask to write random bytes to a symbolic buffer")
|
||||
raise ConcretizeArgument(platform.current, 0)
|
||||
|
||||
+778
-468
File diff suppressed because it is too large
Load Diff
+425
-370
File diff suppressed because it is too large
Load Diff
@@ -20,8 +20,10 @@ class SyscallStubs:
|
||||
self.parent = parent
|
||||
|
||||
def __getattr__(self, item):
|
||||
print("System calls should be copied and pasted into linux.py, not implemented within the stub file.",
|
||||
"If you're seeing this message, you may have forgotten to do that.")
|
||||
print(
|
||||
"System calls should be copied and pasted into linux.py, not implemented within the stub file.",
|
||||
"If you're seeing this message, you may have forgotten to do that.",
|
||||
)
|
||||
return getattr(self.parent, item)
|
||||
|
||||
def simple_returns(self):
|
||||
|
||||
@@ -11,12 +11,14 @@ class OSException(Exception):
|
||||
|
||||
@wrapt.decorator
|
||||
def unimplemented(wrapped, _instance, args, kwargs):
|
||||
cpu = getattr(getattr(_instance, 'parent', None), 'current', None)
|
||||
addr = None if cpu is None else cpu.read_register('PC')
|
||||
logger.warning(f"Unimplemented system call%s: %s(%s)",
|
||||
'' if addr is None else ' at ' + hex(addr),
|
||||
wrapped.__name__,
|
||||
', '.join(hex(a) for a in args))
|
||||
cpu = getattr(getattr(_instance, "parent", None), "current", None)
|
||||
addr = None if cpu is None else cpu.read_register("PC")
|
||||
logger.warning(
|
||||
f"Unimplemented system call%s: %s(%s)",
|
||||
"" if addr is None else " at " + hex(addr),
|
||||
wrapped.__name__,
|
||||
", ".join(hex(a) for a in args),
|
||||
)
|
||||
return wrapped(*args, **kwargs)
|
||||
|
||||
|
||||
@@ -32,7 +34,7 @@ class SyscallNotImplemented(OSException):
|
||||
|
||||
|
||||
class ConcretizeSyscallArgument(OSException):
|
||||
def __init__(self, reg_num, message='Concretizing syscall argument', policy='SAMPLED'):
|
||||
def __init__(self, reg_num, message="Concretizing syscall argument", policy="SAMPLED"):
|
||||
self.reg_num = reg_num
|
||||
self.message = message
|
||||
self.policy = policy
|
||||
|
||||
@@ -7,10 +7,10 @@ from prettytable import PrettyTable
|
||||
from ..ethereum.detectors import DetectorClassification
|
||||
|
||||
classification_txt = {
|
||||
DetectorClassification.INFORMATIONAL: 'Informational',
|
||||
DetectorClassification.LOW: 'Low',
|
||||
DetectorClassification.MEDIUM: 'Medium',
|
||||
DetectorClassification.HIGH: 'High',
|
||||
DetectorClassification.INFORMATIONAL: "Informational",
|
||||
DetectorClassification.LOW: "Low",
|
||||
DetectorClassification.MEDIUM: "Medium",
|
||||
DetectorClassification.HIGH: "High",
|
||||
}
|
||||
|
||||
|
||||
@@ -28,14 +28,12 @@ def output_detectors(detector_classes):
|
||||
confidence = classification_txt[detector.CONFIDENCE]
|
||||
detectors_list.append((argument, help_info, impact, confidence))
|
||||
|
||||
table = PrettyTable(["Num",
|
||||
"Check",
|
||||
"What it Detects",
|
||||
"Impact",
|
||||
"Confidence"])
|
||||
table = PrettyTable(["Num", "Check", "What it Detects", "Impact", "Confidence"])
|
||||
|
||||
# Sort by impact, confidence, and name
|
||||
detectors_list = sorted(detectors_list, key=lambda element: (element[2], element[3], element[0]))
|
||||
detectors_list = sorted(
|
||||
detectors_list, key=lambda element: (element[2], element[3], element[0])
|
||||
)
|
||||
idx = 1
|
||||
for (argument, help_info, impact, confidence) in detectors_list:
|
||||
table.add_row([idx, argument, help_info, classification_txt[impact], confidence])
|
||||
|
||||
+25
-20
@@ -27,7 +27,7 @@ class ConfigError(Exception):
|
||||
|
||||
|
||||
class _Var:
|
||||
def __init__(self, name: str='', default=None, description: str=None, defined: bool=True):
|
||||
def __init__(self, name: str = "", default=None, description: str = None, defined: bool = True):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.value = default
|
||||
@@ -58,16 +58,17 @@ class _Group:
|
||||
Also don't forget that a given variable can be set through CLI or .yaml file!
|
||||
(see config.py)
|
||||
"""
|
||||
|
||||
def __init__(self, name: str):
|
||||
# To bypass __setattr__
|
||||
object.__setattr__(self, '_name', name)
|
||||
object.__setattr__(self, '_vars', {})
|
||||
object.__setattr__(self, "_name", name)
|
||||
object.__setattr__(self, "_vars", {})
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
|
||||
def add(self, name: str, default=None, description: str=None):
|
||||
def add(self, name: str, default=None, description: str = None):
|
||||
"""
|
||||
Add a variable named |name| to this value group, optionally giving it a
|
||||
default value and a description.
|
||||
@@ -80,13 +81,13 @@ class _Group:
|
||||
if name in self._vars:
|
||||
raise ConfigError(f"{self.name}.{name} already defined.")
|
||||
|
||||
if name == 'name':
|
||||
if name == "name":
|
||||
raise ConfigError("'name' is a reserved name for a group.")
|
||||
|
||||
v = _Var(name, description=description, default=default)
|
||||
self._vars[name] = v
|
||||
|
||||
def update(self, name: str, value=None, default=None, description: str=None):
|
||||
def update(self, name: str, value=None, default=None, description: str = None):
|
||||
"""
|
||||
Like add, but can tolerate existing values; also updates the value.
|
||||
|
||||
@@ -95,7 +96,7 @@ class _Group:
|
||||
if name in self._vars:
|
||||
description = description or self._vars[name].description
|
||||
default = default or self._vars[name].default
|
||||
elif name == 'name':
|
||||
elif name == "name":
|
||||
raise ConfigError("'name' is a reserved name for a group.")
|
||||
|
||||
v = _Var(name, description=description, default=default, defined=False)
|
||||
@@ -152,9 +153,9 @@ class _Group:
|
||||
|
||||
class _TemporaryGroup:
|
||||
def __init__(self, group: _Group):
|
||||
object.__setattr__(self, '_group', group)
|
||||
object.__setattr__(self, '_entered', False)
|
||||
object.__setattr__(self, '_saved', {k: v.value for k, v in group._vars.items()})
|
||||
object.__setattr__(self, "_group", group)
|
||||
object.__setattr__(self, "_entered", False)
|
||||
object.__setattr__(self, "_saved", {k: v.value for k, v in group._vars.items()})
|
||||
|
||||
def __getattr__(self, item):
|
||||
return getattr(self._grp, item)
|
||||
@@ -167,13 +168,13 @@ class _TemporaryGroup:
|
||||
if self._entered is True:
|
||||
raise ConfigError("Can't use temporary group recursively!")
|
||||
|
||||
object.__setattr__(self, '_entered', True)
|
||||
object.__setattr__(self, "_entered", True)
|
||||
|
||||
def __exit__(self, *_):
|
||||
for k in self._saved:
|
||||
setattr(self._group, k, self._saved[k])
|
||||
|
||||
object.__setattr__(self, '_entered', False)
|
||||
object.__setattr__(self, "_entered", False)
|
||||
|
||||
|
||||
def get_group(name: str) -> _Group:
|
||||
@@ -250,13 +251,13 @@ def load_overrides(path=None):
|
||||
if path is not None:
|
||||
names = [path]
|
||||
else:
|
||||
possible_names = ['mcore.yml', 'manticore.yml']
|
||||
names = [os.path.join('.', ''.join(x)) for x in product(['', '.'], possible_names)]
|
||||
possible_names = ["mcore.yml", "manticore.yml"]
|
||||
names = [os.path.join(".", "".join(x)) for x in product(["", "."], possible_names)]
|
||||
|
||||
for name in names:
|
||||
try:
|
||||
with open(name, 'r') as yml_f:
|
||||
logger.info(f'Reading configuration from {name}')
|
||||
with open(name, "r") as yml_f:
|
||||
logger.info(f"Reading configuration from {name}")
|
||||
parse_config(yml_f)
|
||||
break
|
||||
except FileNotFoundError:
|
||||
@@ -277,8 +278,12 @@ def add_config_vars_to_argparse(args):
|
||||
for group_name, group in _groups.items():
|
||||
for key in group:
|
||||
obj = group._var_object(key)
|
||||
args.add_argument(f"--{group_name}.{key}", type=type(obj.default),
|
||||
default=obj.default, help=obj.description)
|
||||
args.add_argument(
|
||||
f"--{group_name}.{key}",
|
||||
type=type(obj.default),
|
||||
default=obj.default,
|
||||
help=obj.description,
|
||||
)
|
||||
|
||||
|
||||
def process_config_values(parser: argparse.ArgumentParser, args: argparse.Namespace):
|
||||
@@ -299,7 +304,7 @@ def process_config_values(parser: argparse.ArgumentParser, args: argparse.Namesp
|
||||
command_line_args = vars(args)
|
||||
|
||||
# Bring in the options keys into args
|
||||
config_cli_args = get_group('cli')
|
||||
config_cli_args = get_group("cli")
|
||||
|
||||
# Place all command line args into the cli group (for saving in the workspace). If
|
||||
# the value is set on command line, then it takes precedence; otherwise we try to
|
||||
@@ -312,7 +317,7 @@ def process_config_values(parser: argparse.ArgumentParser, args: argparse.Namesp
|
||||
config_cli_args.update(k, value=set_val)
|
||||
else:
|
||||
# Update a var's native group
|
||||
group_name, key = k.split('.')
|
||||
group_name, key = k.split(".")
|
||||
group = get_group(group_name)
|
||||
setattr(group, key, set_val)
|
||||
else:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
from functools import wraps
|
||||
import inspect
|
||||
import warnings
|
||||
@@ -8,10 +7,11 @@ import warnings
|
||||
|
||||
class ManticoreDeprecationWarning(DeprecationWarning):
|
||||
"""The deprecation warning class used by Manticore."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
warnings.simplefilter('default', category=ManticoreDeprecationWarning)
|
||||
warnings.simplefilter("default", category=ManticoreDeprecationWarning)
|
||||
|
||||
|
||||
def deprecated(message: str):
|
||||
@@ -21,8 +21,11 @@ def deprecated(message: str):
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
warnings.warn(f"`{func.__qualname__}` is deprecated. {message}",
|
||||
category=ManticoreDeprecationWarning, stacklevel=2)
|
||||
warnings.warn(
|
||||
f"`{func.__qualname__}` is deprecated. {message}",
|
||||
category=ManticoreDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
+72
-44
@@ -2,6 +2,7 @@ import logging
|
||||
import time
|
||||
|
||||
from capstone import *
|
||||
|
||||
######################################################################
|
||||
# Abstract classes for capstone/unicorn based cpus
|
||||
# no emulator by default
|
||||
@@ -23,16 +24,16 @@ def convert_permissions(m_perms):
|
||||
:return: Unicorn Permissions
|
||||
"""
|
||||
permissions = UC_PROT_NONE
|
||||
if 'r' in m_perms:
|
||||
if "r" in m_perms:
|
||||
permissions |= UC_PROT_READ
|
||||
if 'w' in m_perms:
|
||||
if "w" in m_perms:
|
||||
permissions |= UC_PROT_WRITE
|
||||
if 'x' in m_perms:
|
||||
if "x" in m_perms:
|
||||
permissions |= UC_PROT_EXEC
|
||||
return permissions
|
||||
|
||||
|
||||
def hr_size(num, suffix='B') -> str:
|
||||
def hr_size(num, suffix="B") -> str:
|
||||
"""
|
||||
Human-readable data size
|
||||
From https://stackoverflow.com/a/1094933
|
||||
@@ -40,11 +41,11 @@ def hr_size(num, suffix='B') -> str:
|
||||
:param suffix: Optional size specifier
|
||||
:return: Formatted string
|
||||
"""
|
||||
for unit in ' KMGTPEZ':
|
||||
for unit in " KMGTPEZ":
|
||||
if abs(num) < 1024.0:
|
||||
return "%3.1f%s%s" % (num, unit if unit != ' ' else '', suffix)
|
||||
return "%3.1f%s%s" % (num, unit if unit != " " else "", suffix)
|
||||
num /= 1024.0
|
||||
return "%.1f%s%s" % (num, 'Y', suffix)
|
||||
return "%.1f%s%s" % (num, "Y", suffix)
|
||||
|
||||
|
||||
class ConcreteUnicornEmulator:
|
||||
@@ -66,28 +67,26 @@ class ConcreteUnicornEmulator:
|
||||
|
||||
Only supports X86_64 for now.
|
||||
"""
|
||||
|
||||
def __init__(self, cpu):
|
||||
self._cpu = cpu
|
||||
self._mem_delta = {}
|
||||
self.flag_registers = {'CF', 'PF', 'AF', 'ZF', 'SF', 'IF', 'DF', 'OF'}
|
||||
self.flag_registers = {"CF", "PF", "AF", "ZF", "SF", "IF", "DF", "OF"}
|
||||
self.write_backs_disabled = False
|
||||
self._stop_at = None
|
||||
|
||||
cpu.subscribe('did_write_memory', self.write_back_memory)
|
||||
cpu.subscribe('did_write_register', self.write_back_register)
|
||||
cpu.subscribe('did_set_descriptor', self.update_segment)
|
||||
cpu.subscribe('did_map_memory', self.map_memory_callback)
|
||||
cpu.subscribe('did_unmap_memory', self.unmap_memory_callback)
|
||||
cpu.subscribe('did_protect_memory', self.protect_memory_callback)
|
||||
cpu.subscribe("did_write_memory", self.write_back_memory)
|
||||
cpu.subscribe("did_write_register", self.write_back_register)
|
||||
cpu.subscribe("did_set_descriptor", self.update_segment)
|
||||
cpu.subscribe("did_map_memory", self.map_memory_callback)
|
||||
cpu.subscribe("did_unmap_memory", self.unmap_memory_callback)
|
||||
cpu.subscribe("did_protect_memory", self.protect_memory_callback)
|
||||
|
||||
if self._cpu.arch == CS_ARCH_X86:
|
||||
self._uc_arch = UC_ARCH_X86
|
||||
self._uc_mode = {
|
||||
CS_MODE_32: UC_MODE_32,
|
||||
CS_MODE_64: UC_MODE_64
|
||||
}[self._cpu.mode]
|
||||
self._uc_mode = {CS_MODE_32: UC_MODE_32, CS_MODE_64: UC_MODE_64}[self._cpu.mode]
|
||||
else:
|
||||
raise NotImplementedError(f'Unsupported architecture: {self._cpu.arch}')
|
||||
raise NotImplementedError(f"Unsupported architecture: {self._cpu.arch}")
|
||||
|
||||
self.reset()
|
||||
|
||||
@@ -103,20 +102,22 @@ class ConcreteUnicornEmulator:
|
||||
self.registers -= self.flag_registers
|
||||
# TODO(eric_k): unicorn@778171fc9546c1fc3d1341ff1151eab379848ea0 doesn't like writing to
|
||||
# the FS register, and it will segfault or hang.
|
||||
self.registers -= {'FS'}
|
||||
self.registers.add('EFLAGS')
|
||||
self.registers -= {"FS"}
|
||||
self.registers.add("EFLAGS")
|
||||
|
||||
for reg in self.registers:
|
||||
val = self._cpu.read_register(reg)
|
||||
|
||||
if reg in {'FS', 'GS'}:
|
||||
if reg in {"FS", "GS"}:
|
||||
self.msr_write(reg, val)
|
||||
continue
|
||||
|
||||
if issymbolic(val):
|
||||
from ..native.cpu.abstractcpu import ConcretizeRegister
|
||||
raise ConcretizeRegister(self._cpu, reg, "Concretizing for emulation.",
|
||||
policy='ONE')
|
||||
|
||||
raise ConcretizeRegister(
|
||||
self._cpu, reg, "Concretizing for emulation.", policy="ONE"
|
||||
)
|
||||
logger.debug("Writing %s into %s", val, reg)
|
||||
self._emu.reg_write(self._to_unicorn_id(reg), val)
|
||||
|
||||
@@ -138,18 +139,29 @@ class ConcreteUnicornEmulator:
|
||||
map_bytes = self._cpu._raw_read(address, size)
|
||||
self._emu.mem_write(address, map_bytes)
|
||||
if time.time() - start_time > 3:
|
||||
logger.info(f"Copying {hr_size(size)} map at {hex(address)} took {time.time() - start_time} seconds")
|
||||
logger.info(
|
||||
f"Copying {hr_size(size)} map at {hex(address)} took {time.time() - start_time} seconds"
|
||||
)
|
||||
|
||||
def map_memory_callback(self, address, size, perms, name, offset, result):
|
||||
"""
|
||||
Catches did_map_memory and copies the mapping into Manticore
|
||||
"""
|
||||
logger.info(' '.join(("Mapping Memory @",
|
||||
hex(address) if type(address) is int else "0x??",
|
||||
hr_size(size), "-",
|
||||
perms, "-",
|
||||
f"{name}:{hex(offset) if name else ''}", "->",
|
||||
hex(result))))
|
||||
logger.info(
|
||||
" ".join(
|
||||
(
|
||||
"Mapping Memory @",
|
||||
hex(address) if type(address) is int else "0x??",
|
||||
hr_size(size),
|
||||
"-",
|
||||
perms,
|
||||
"-",
|
||||
f"{name}:{hex(offset) if name else ''}",
|
||||
"->",
|
||||
hex(result),
|
||||
)
|
||||
)
|
||||
)
|
||||
self._emu.mem_map(address, size, convert_permissions(perms))
|
||||
self.copy_memory(address, size)
|
||||
|
||||
@@ -187,9 +199,12 @@ class ConcreteUnicornEmulator:
|
||||
"""
|
||||
Unicorn hook that transfers control to Manticore so it can execute the syscall
|
||||
"""
|
||||
logger.debug(f"Stopping emulation at {hex(uc.reg_read(self._to_unicorn_id('RIP')))} to perform syscall")
|
||||
logger.debug(
|
||||
f"Stopping emulation at {hex(uc.reg_read(self._to_unicorn_id('RIP')))} to perform syscall"
|
||||
)
|
||||
self.sync_unicorn_to_manticore()
|
||||
from ..native.cpu.abstractcpu import Syscall
|
||||
|
||||
self._to_raise = Syscall()
|
||||
uc.emu_stop()
|
||||
|
||||
@@ -210,7 +225,11 @@ class ConcreteUnicornEmulator:
|
||||
m = self._cpu.memory.map_containing(address)
|
||||
self.copy_memory(m.start, m.end - m.start)
|
||||
except MemoryException as e:
|
||||
logger.error("Failed to map memory {}-{}, ({}): {}".format(hex(address), hex(address + size), access, e))
|
||||
logger.error(
|
||||
"Failed to map memory {}-{}, ({}): {}".format(
|
||||
hex(address), hex(address + size), access, e
|
||||
)
|
||||
)
|
||||
self._to_raise = e
|
||||
self._should_try_again = False
|
||||
return False
|
||||
@@ -224,17 +243,18 @@ class ConcreteUnicornEmulator:
|
||||
"""
|
||||
logger.info("Caught interrupt: %s" % number)
|
||||
from ..native.cpu.abstractcpu import Interruption # prevent circular imports
|
||||
|
||||
self._to_raise = Interruption(number)
|
||||
return True
|
||||
|
||||
def _to_unicorn_id(self, reg_name):
|
||||
if self._cpu.arch == CS_ARCH_ARM:
|
||||
return globals()['UC_ARM_REG_' + reg_name]
|
||||
return globals()["UC_ARM_REG_" + reg_name]
|
||||
elif self._cpu.arch == CS_ARCH_X86:
|
||||
# TODO(yan): This needs to handle AF register
|
||||
custom_mapping = {'PC': 'RIP', 'STACK': 'RSP'}
|
||||
custom_mapping = {"PC": "RIP", "STACK": "RSP"}
|
||||
try:
|
||||
return globals()['UC_X86_REG_' + custom_mapping.get(reg_name, reg_name)]
|
||||
return globals()["UC_X86_REG_" + custom_mapping.get(reg_name, reg_name)]
|
||||
except KeyError:
|
||||
logger.error("Can't find register UC_X86_REG_%s", str(reg_name))
|
||||
raise
|
||||
@@ -293,6 +313,7 @@ class ConcreteUnicornEmulator:
|
||||
# Raise the exception from a hook that Unicorn would have eaten
|
||||
if self._to_raise:
|
||||
from ..native.cpu.abstractcpu import Syscall
|
||||
|
||||
if type(self._to_raise) is not Syscall:
|
||||
logger.info("Raising %s", self._to_raise)
|
||||
raise self._to_raise
|
||||
@@ -324,7 +345,10 @@ class ConcreteUnicornEmulator:
|
||||
self._emu.mem_write(where, expr)
|
||||
else:
|
||||
if issymbolic(expr):
|
||||
data = [Operators.CHR(Operators.EXTRACT(expr, offset, 8)) for offset in range(0, size, 8)]
|
||||
data = [
|
||||
Operators.CHR(Operators.EXTRACT(expr, offset, 8))
|
||||
for offset in range(0, size, 8)
|
||||
]
|
||||
concrete_data = []
|
||||
for c in data:
|
||||
if issymbolic(c):
|
||||
@@ -332,10 +356,15 @@ class ConcreteUnicornEmulator:
|
||||
concrete_data.append(c)
|
||||
data = concrete_data
|
||||
else:
|
||||
data = [Operators.CHR(Operators.EXTRACT(expr, offset, 8)) for offset in range(0, size, 8)]
|
||||
data = [
|
||||
Operators.CHR(Operators.EXTRACT(expr, offset, 8))
|
||||
for offset in range(0, size, 8)
|
||||
]
|
||||
logger.debug(f"Writing back {hr_size(size // 8)} to {hex(where)}: {data}")
|
||||
# TODO - the extra encoding is to handle null bytes output as strings when we concretize. That's probably a bug.
|
||||
self._emu.mem_write(where, b''.join(b.encode('utf-8') if type(b) is str else b for b in data))
|
||||
self._emu.mem_write(
|
||||
where, b"".join(b.encode("utf-8") if type(b) is str else b for b in data)
|
||||
)
|
||||
|
||||
def write_back_register(self, reg, val):
|
||||
""" Sync register state from Manticore -> Unicorn"""
|
||||
@@ -345,11 +374,11 @@ class ConcreteUnicornEmulator:
|
||||
logger.warning("Skipping Symbolic write-back")
|
||||
return
|
||||
if reg in self.flag_registers:
|
||||
self._emu.reg_write(self._to_unicorn_id('EFLAGS'), self._cpu.read_register('EFLAGS'))
|
||||
self._emu.reg_write(self._to_unicorn_id("EFLAGS"), self._cpu.read_register("EFLAGS"))
|
||||
return
|
||||
# TODO(eric_k): unicorn@778171fc9546c1fc3d1341ff1151eab379848ea0 doesn't like writing to
|
||||
# the FS register, and it will segfault or hang.
|
||||
if reg in {'FS'}:
|
||||
if reg in {"FS"}:
|
||||
logger.warning(f"Skipping {reg} write. Unicorn unsupported register write.")
|
||||
return
|
||||
self._emu.reg_write(self._to_unicorn_id(reg), val)
|
||||
@@ -358,7 +387,7 @@ class ConcreteUnicornEmulator:
|
||||
""" Only useful for setting FS right now. """
|
||||
logger.info("Updating selector %s to 0x%02x (%s bytes) (%s)", selector, base, size, perms)
|
||||
if selector == 99:
|
||||
self.msr_write('FS', base)
|
||||
self.msr_write("FS", base)
|
||||
else:
|
||||
logger.error("No way to write segment: %d", selector)
|
||||
|
||||
@@ -367,6 +396,5 @@ class ConcreteUnicornEmulator:
|
||||
set the hidden descriptor-register fields to the given address.
|
||||
This enables referencing the fs segment on x86-64.
|
||||
"""
|
||||
magic = {'FS': 0xC0000100,
|
||||
'GS': 0xC0000101}
|
||||
magic = {"FS": 0xC0000100, "GS": 0xC0000101}
|
||||
return self._emu.msr_write(magic[reg], data)
|
||||
|
||||
@@ -12,12 +12,13 @@ class EventsGatherMetaclass(type):
|
||||
Metaclass that is used for Eventful to gather events that classes declare to
|
||||
publish.
|
||||
"""
|
||||
|
||||
def __new__(cls, name, parents, d):
|
||||
eventful_sub = super(EventsGatherMetaclass, cls).__new__(cls, name, parents, d)
|
||||
|
||||
bases = inspect.getmro(parents[0])
|
||||
|
||||
if name is 'Eventful':
|
||||
if name is "Eventful":
|
||||
return eventful_sub
|
||||
|
||||
subclasses = takewhile(lambda c: c is not Eventful, bases)
|
||||
@@ -29,7 +30,7 @@ class EventsGatherMetaclass(type):
|
||||
for sub in relevant_classes:
|
||||
# Not using hasattr() here because we only want classes where it's explicitly
|
||||
# defined.
|
||||
if '_published_events' in sub.__dict__:
|
||||
if "_published_events" in sub.__dict__:
|
||||
relevant_events.update(sub._published_events)
|
||||
Eventful.__all_events__[eventful_sub] = relevant_events
|
||||
|
||||
@@ -59,7 +60,7 @@ class Eventful(object, metaclass=EventsGatherMetaclass):
|
||||
_published_events = set()
|
||||
|
||||
# Event names prefixes
|
||||
prefixes = ('will_', 'did_', 'on_')
|
||||
prefixes = ("will_", "did_", "on_")
|
||||
|
||||
@classmethod
|
||||
def all_events(cls):
|
||||
@@ -111,7 +112,7 @@ class Eventful(object, metaclass=EventsGatherMetaclass):
|
||||
basename = _name
|
||||
for prefix in self.prefixes:
|
||||
if _name.startswith(prefix):
|
||||
basename = _name[len(prefix):]
|
||||
basename = _name[len(prefix) :]
|
||||
|
||||
cls = self.__class__
|
||||
if basename not in cls.__all_events__[cls]:
|
||||
@@ -144,7 +145,7 @@ class Eventful(object, metaclass=EventsGatherMetaclass):
|
||||
sink._publish_impl(_name, *args, **kwargs)
|
||||
|
||||
def subscribe(self, name, method):
|
||||
assert inspect.ismethod(method), f'{method.__class__.__name__} is not a method'
|
||||
assert inspect.ismethod(method), f"{method.__class__.__name__} is not a method"
|
||||
obj, callback = method.__self__, method.__func__
|
||||
bucket = self._get_signal_bucket(name)
|
||||
robj = ref(obj, self._unref) # see unref() for explanation
|
||||
@@ -152,14 +153,14 @@ class Eventful(object, metaclass=EventsGatherMetaclass):
|
||||
self.__sub_events__.add(name)
|
||||
|
||||
def forward_events_from(self, source, include_source=False):
|
||||
assert isinstance(source, Eventful), f'{source.__class__.__name__} is not Eventful'
|
||||
assert isinstance(source, Eventful), f"{source.__class__.__name__} is not Eventful"
|
||||
source.forward_events_to(self, include_source=include_source)
|
||||
|
||||
def forward_events_to(self, sink, include_source=False):
|
||||
"""This forwards signal to sink"""
|
||||
assert isinstance(sink, Eventful), f'{sink.__class__.__name__} is not Eventful'
|
||||
assert isinstance(sink, Eventful), f"{sink.__class__.__name__} is not Eventful"
|
||||
self._forwards[sink] = include_source
|
||||
|
||||
def copy_eventful_state(self, new_object: 'Eventful'):
|
||||
def copy_eventful_state(self, new_object: "Eventful"):
|
||||
new_object._forwards = copy.copy(self._forwards)
|
||||
new_object._signals = copy.copy(self._signals)
|
||||
|
||||
@@ -3,6 +3,7 @@ import logging
|
||||
from ..native.memory import MemoryException
|
||||
|
||||
from .helpers import issymbolic
|
||||
|
||||
######################################################################
|
||||
# Abstract classes for capstone/unicorn based cpus
|
||||
# no emulator by default
|
||||
@@ -26,9 +27,7 @@ class UnicornEmulator:
|
||||
|
||||
text = cpu.memory.map_containing(cpu.PC)
|
||||
# Keep track of all memory mappings. We start with just the text section
|
||||
self._should_be_mapped = {
|
||||
text.start: (len(text), UC_PROT_READ | UC_PROT_EXEC)
|
||||
}
|
||||
self._should_be_mapped = {text.start: (len(text), UC_PROT_READ | UC_PROT_EXEC)}
|
||||
|
||||
# Keep track of all the memory Unicorn needs while executing this
|
||||
# instruction
|
||||
@@ -36,26 +35,20 @@ class UnicornEmulator:
|
||||
|
||||
if self._cpu.arch == CS_ARCH_ARM:
|
||||
self._uc_arch = UC_ARCH_ARM
|
||||
self._uc_mode = {
|
||||
CS_MODE_ARM: UC_MODE_ARM,
|
||||
CS_MODE_THUMB: UC_MODE_THUMB
|
||||
}[self._cpu.mode]
|
||||
self._uc_mode = {CS_MODE_ARM: UC_MODE_ARM, CS_MODE_THUMB: UC_MODE_THUMB}[self._cpu.mode]
|
||||
|
||||
elif self._cpu.arch == CS_ARCH_ARM64:
|
||||
self._uc_arch = UC_ARCH_ARM64
|
||||
self._uc_mode = UC_MODE_ARM
|
||||
if self._cpu.mode != UC_MODE_ARM:
|
||||
raise Exception('Aarch64/Arm64 cannot have different uc mode than ARM.')
|
||||
raise Exception("Aarch64/Arm64 cannot have different uc mode than ARM.")
|
||||
|
||||
elif self._cpu.arch == CS_ARCH_X86:
|
||||
self._uc_arch = UC_ARCH_X86
|
||||
self._uc_mode = {
|
||||
CS_MODE_32: UC_MODE_32,
|
||||
CS_MODE_64: UC_MODE_64
|
||||
}[self._cpu.mode]
|
||||
self._uc_mode = {CS_MODE_32: UC_MODE_32, CS_MODE_64: UC_MODE_64}[self._cpu.mode]
|
||||
|
||||
else:
|
||||
raise NotImplementedError(f'Unsupported architecture: {self._cpu.arch}')
|
||||
raise NotImplementedError(f"Unsupported architecture: {self._cpu.arch}")
|
||||
|
||||
def reset(self):
|
||||
self._emu = Uc(self._uc_arch, self._uc_mode)
|
||||
@@ -72,11 +65,11 @@ class UnicornEmulator:
|
||||
m = self._cpu.memory.map_containing(address)
|
||||
|
||||
permissions = UC_PROT_NONE
|
||||
if 'r' in m.perms:
|
||||
if "r" in m.perms:
|
||||
permissions |= UC_PROT_READ
|
||||
if 'w' in m.perms:
|
||||
if "w" in m.perms:
|
||||
permissions |= UC_PROT_WRITE
|
||||
if 'x' in m.perms:
|
||||
if "x" in m.perms:
|
||||
permissions |= UC_PROT_EXEC
|
||||
|
||||
uc.mem_map(m.start, len(m), permissions)
|
||||
@@ -96,7 +89,9 @@ class UnicornEmulator:
|
||||
elif self._cpu.mode == CS_MODE_64:
|
||||
return self._emu.reg_read(UC_X86_REG_RIP)
|
||||
else:
|
||||
raise Exception(f"Getting PC after unicorn emulation for {self._cpu.arch} architecture is not implemented")
|
||||
raise Exception(
|
||||
f"Getting PC after unicorn emulation for {self._cpu.arch} architecture is not implemented"
|
||||
)
|
||||
|
||||
def _hook_xfer_mem(self, uc, access, address, size, value, data):
|
||||
"""
|
||||
@@ -145,6 +140,7 @@ class UnicornEmulator:
|
||||
"""
|
||||
|
||||
from ..native.cpu.abstractcpu import Interruption # prevent circular imports
|
||||
|
||||
self._to_raise = Interruption(number)
|
||||
return True
|
||||
|
||||
@@ -152,15 +148,15 @@ class UnicornEmulator:
|
||||
# TODO(felipe, yan): Register naming is broken in current unicorn
|
||||
# packages, but works on unicorn git's master. We leave this hack
|
||||
# in until unicorn gets updated.
|
||||
if unicorn.__version__ <= '1.0.0' and reg_name == 'APSR':
|
||||
reg_name = 'CPSR'
|
||||
if unicorn.__version__ <= "1.0.0" and reg_name == "APSR":
|
||||
reg_name = "CPSR"
|
||||
if self._cpu.arch == CS_ARCH_ARM:
|
||||
return globals()['UC_ARM_REG_' + reg_name]
|
||||
return globals()["UC_ARM_REG_" + reg_name]
|
||||
elif self._cpu.arch == CS_ARCH_ARM64:
|
||||
return globals()['UC_ARM64_REG_' + reg_name]
|
||||
return globals()["UC_ARM64_REG_" + reg_name]
|
||||
elif self._cpu.arch == CS_ARCH_X86:
|
||||
# TODO(yan): This needs to handle AF register
|
||||
return globals()['UC_X86_REG_' + reg_name]
|
||||
return globals()["UC_X86_REG_" + reg_name]
|
||||
else:
|
||||
# TODO(yan): raise a more appropriate exception
|
||||
raise TypeError(f"Cannot convert {reg_name} to unicorn register id")
|
||||
@@ -193,10 +189,12 @@ class UnicornEmulator:
|
||||
for offset, byte in enumerate(values, start=address):
|
||||
if issymbolic(byte):
|
||||
from ..native.cpu.abstractcpu import ConcretizeMemory
|
||||
raise ConcretizeMemory(self._cpu.memory, offset, 8,
|
||||
"Concretizing for emulation")
|
||||
|
||||
self._emu.mem_write(address, b''.join(values))
|
||||
raise ConcretizeMemory(
|
||||
self._cpu.memory, offset, 8, "Concretizing for emulation"
|
||||
)
|
||||
|
||||
self._emu.mem_write(address, b"".join(values))
|
||||
|
||||
# Try emulation
|
||||
self._should_try_again = False
|
||||
@@ -210,8 +208,9 @@ class UnicornEmulator:
|
||||
"""
|
||||
A single attempt at executing an instruction.
|
||||
"""
|
||||
logger.debug("0x%x:\t%s\t%s"
|
||||
% (instruction.address, instruction.mnemonic, instruction.op_str))
|
||||
logger.debug(
|
||||
"0x%x:\t%s\t%s" % (instruction.address, instruction.mnemonic, instruction.op_str)
|
||||
)
|
||||
|
||||
registers = set(self._cpu.canonical_registers)
|
||||
|
||||
@@ -219,8 +218,8 @@ class UnicornEmulator:
|
||||
if self._cpu.arch == CS_ARCH_X86:
|
||||
# The last 8 canonical registers of x86 are individual flags; replace
|
||||
# with the eflags
|
||||
registers -= set(['CF', 'PF', 'AF', 'ZF', 'SF', 'IF', 'DF', 'OF'])
|
||||
registers.add('EFLAGS')
|
||||
registers -= set(["CF", "PF", "AF", "ZF", "SF", "IF", "DF", "OF"])
|
||||
registers.add("EFLAGS")
|
||||
|
||||
# TODO(mark): Unicorn 1.0.1 does not support reading YMM registers,
|
||||
# and simply returns back zero. If a unicorn emulated instruction writes to an
|
||||
@@ -228,14 +227,50 @@ class UnicornEmulator:
|
||||
# incorrect zero value being actually written to the XMM register. This is
|
||||
# fixed in Unicorn PR #819, so when that is included in a release, delete
|
||||
# these two lines.
|
||||
registers -= set(['YMM0', 'YMM1', 'YMM2', 'YMM3', 'YMM4', 'YMM5', 'YMM6', 'YMM7',
|
||||
'YMM8', 'YMM9', 'YMM10', 'YMM11', 'YMM12', 'YMM13', 'YMM14', 'YMM15'])
|
||||
registers |= set(['XMM0', 'XMM1', 'XMM2', 'XMM3', 'XMM4', 'XMM5', 'XMM6', 'XMM7',
|
||||
'XMM8', 'XMM9', 'XMM10', 'XMM11', 'XMM12', 'XMM13', 'XMM14', 'XMM15'])
|
||||
registers -= set(
|
||||
[
|
||||
"YMM0",
|
||||
"YMM1",
|
||||
"YMM2",
|
||||
"YMM3",
|
||||
"YMM4",
|
||||
"YMM5",
|
||||
"YMM6",
|
||||
"YMM7",
|
||||
"YMM8",
|
||||
"YMM9",
|
||||
"YMM10",
|
||||
"YMM11",
|
||||
"YMM12",
|
||||
"YMM13",
|
||||
"YMM14",
|
||||
"YMM15",
|
||||
]
|
||||
)
|
||||
registers |= set(
|
||||
[
|
||||
"XMM0",
|
||||
"XMM1",
|
||||
"XMM2",
|
||||
"XMM3",
|
||||
"XMM4",
|
||||
"XMM5",
|
||||
"XMM6",
|
||||
"XMM7",
|
||||
"XMM8",
|
||||
"XMM9",
|
||||
"XMM10",
|
||||
"XMM11",
|
||||
"XMM12",
|
||||
"XMM13",
|
||||
"XMM14",
|
||||
"XMM15",
|
||||
]
|
||||
)
|
||||
|
||||
# TODO(eric_k): unicorn@778171fc9546c1fc3d1341ff1151eab379848ea0 doesn't like writing to
|
||||
# the FS register, and it will segfault or hang.
|
||||
registers -= {'FS'}
|
||||
registers -= {"FS"}
|
||||
|
||||
# XXX(yan): This concretizes the entire register state. This is overly
|
||||
# aggressive. Once capstone adds consistent support for accessing
|
||||
@@ -245,14 +280,16 @@ class UnicornEmulator:
|
||||
val = self._cpu.read_register(reg)
|
||||
if issymbolic(val):
|
||||
from ..native.cpu.abstractcpu import ConcretizeRegister
|
||||
raise ConcretizeRegister(self._cpu, reg, "Concretizing for emulation.",
|
||||
policy='ONE')
|
||||
|
||||
raise ConcretizeRegister(
|
||||
self._cpu, reg, "Concretizing for emulation.", policy="ONE"
|
||||
)
|
||||
self._emu.reg_write(self._to_unicorn_id(reg), val)
|
||||
|
||||
# Bring in the instruction itself
|
||||
instruction = self._cpu.decode_instruction(self._cpu.PC)
|
||||
text_bytes = self._cpu.read_bytes(self._cpu.PC, instruction.size)
|
||||
self._emu.mem_write(self._cpu.PC, b''.join(text_bytes))
|
||||
self._emu.mem_write(self._cpu.PC, b"".join(text_bytes))
|
||||
|
||||
self._emu.hook_add(UC_HOOK_MEM_READ_UNMAPPED, self._hook_unmapped)
|
||||
self._emu.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED, self._hook_unmapped)
|
||||
@@ -272,8 +309,9 @@ class UnicornEmulator:
|
||||
# Unfortunately, this may lead to race conditions if the value is
|
||||
# too small. Tests that work fine without 'timeout' start to fail
|
||||
# because registers are not being written to.
|
||||
self._emu.emu_start(pc, self._cpu.PC + instruction.size,
|
||||
count=1, timeout=1000000) # microseconds
|
||||
self._emu.emu_start(
|
||||
pc, self._cpu.PC + instruction.size, count=1, timeout=1000000
|
||||
) # microseconds
|
||||
except UcError as e:
|
||||
# We request re-execution by signaling error; if we we didn't set
|
||||
# _should_try_again, it was likely an actual error
|
||||
|
||||
@@ -20,6 +20,7 @@ def issymbolic(value):
|
||||
:rtype: bool
|
||||
"""
|
||||
from ..core.smtlib import Expression # prevent circular imports
|
||||
|
||||
return isinstance(value, Expression)
|
||||
|
||||
|
||||
@@ -107,7 +108,8 @@ class CacheDict(OrderedDict):
|
||||
def __del__(self):
|
||||
log = logging.getLogger(self.__class__.__name__)
|
||||
log.debug(
|
||||
f'DictCache: hits: {self._hits}, misses: {self._misses}, flushes: {self._flushes}, size: {self.__len__()}')
|
||||
f"DictCache: hits: {self._hits}, misses: {self._misses}, flushes: {self._flushes}, size: {self.__len__()}"
|
||||
)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if len(self) > self._max_size:
|
||||
@@ -160,8 +162,10 @@ class PickleSerializer(StateSerializer):
|
||||
except RuntimeError:
|
||||
new_limit = sys.getrecursionlimit() * 2
|
||||
if new_limit > PickleSerializer.MAX_RECURSION:
|
||||
raise Exception(f'PickleSerializer recursion limit surpassed {PickleSerializer.MAX_RECURSION}, aborting')
|
||||
logger.info(f'Recursion soft limit {sys.getrecursionlimit()} hit, increasing')
|
||||
raise Exception(
|
||||
f"PickleSerializer recursion limit surpassed {PickleSerializer.MAX_RECURSION}, aborting"
|
||||
)
|
||||
logger.info(f"Recursion soft limit {sys.getrecursionlimit()} hit, increasing")
|
||||
sys.setrecursionlimit(new_limit)
|
||||
self.serialize(state, f)
|
||||
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
REQUIREMENTS_TO_IMPORTS = {
|
||||
'native': {
|
||||
'capstone': 'capstone',
|
||||
'pyelftools': 'elftools',
|
||||
'unicorn': 'unicorn'
|
||||
}
|
||||
"native": {"capstone": "capstone", "pyelftools": "elftools", "unicorn": "unicorn"}
|
||||
}
|
||||
|
||||
|
||||
def ensure_native_deps():
|
||||
if not has_native:
|
||||
raise ImportError(
|
||||
'Missing some packages for native binary analysis. Please install them with pip install manticore[native].'
|
||||
"Missing some packages for native binary analysis. Please install them with pip install manticore[native]."
|
||||
)
|
||||
|
||||
|
||||
@@ -24,6 +20,6 @@ def _has_deps(deps):
|
||||
return True
|
||||
|
||||
|
||||
has_native = _has_deps('native')
|
||||
has_native = _has_deps("native")
|
||||
|
||||
__all__ = ['ensure_native_deps', 'has_native']
|
||||
__all__ = ["ensure_native_deps", "has_native"]
|
||||
|
||||
+36
-35
@@ -5,7 +5,7 @@ manticore_verbosity = 0
|
||||
DEFAULT_LOG_LEVEL = logging.WARNING
|
||||
all_loggers = set()
|
||||
default_factory = logging.getLogRecordFactory()
|
||||
logfmt = ("%(asctime)s: [%(process)d] %(name)s:%(levelname)s %(message)s")
|
||||
logfmt = "%(asctime)s: [%(process)d] %(name)s:%(levelname)s %(message)s"
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
formatter = logging.Formatter(logfmt)
|
||||
handler.setFormatter(formatter)
|
||||
@@ -21,23 +21,26 @@ class ContextFilter(logging.Filter):
|
||||
Produce a summarized record name
|
||||
i.e. manticore.core.executor -> m.c.executor
|
||||
"""
|
||||
components = name.split('.')
|
||||
prefix = '.'.join(c[0] for c in components[:-1])
|
||||
return f'{prefix}.{components[-1]}'
|
||||
components = name.split(".")
|
||||
prefix = ".".join(c[0] for c in components[:-1])
|
||||
return f"{prefix}.{components[-1]}"
|
||||
|
||||
colors_disabled = False
|
||||
|
||||
coloring = {u'DEBUG': u'magenta', u'WARNING': u'yellow',
|
||||
u'ERROR': u'red', u'INFO': u'blue'}
|
||||
colors = dict(zip([u'black', u'red', u'green', u'yellow',
|
||||
u'blue', u'magenta', u'cyan', u'white'], map(str, range(30, 30 + 8))))
|
||||
coloring = {"DEBUG": "magenta", "WARNING": "yellow", "ERROR": "red", "INFO": "blue"}
|
||||
colors = dict(
|
||||
zip(
|
||||
["black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"],
|
||||
map(str, range(30, 30 + 8)),
|
||||
)
|
||||
)
|
||||
|
||||
color_map = {}
|
||||
for k, v in coloring.items():
|
||||
color_map[k] = colors[v]
|
||||
|
||||
colored_levelname_format = u'\x1b[{}m{}:\x1b[0m'
|
||||
plain_levelname_format = u'{}:'
|
||||
colored_levelname_format = "\x1b[{}m{}:\x1b[0m"
|
||||
plain_levelname_format = "{}:"
|
||||
|
||||
def colored_level_name(self, levelname):
|
||||
"""
|
||||
@@ -67,7 +70,7 @@ class CustomLogger(logging.Logger):
|
||||
all_loggers.add(name)
|
||||
self.initialized = False
|
||||
|
||||
if name.startswith('manticore'):
|
||||
if name.startswith("manticore"):
|
||||
self.addHandler(handler)
|
||||
self.addFilter(ctxfilter)
|
||||
self.propagate = False
|
||||
@@ -86,38 +89,36 @@ def get_levels():
|
||||
[(x, DEFAULT_LOG_LEVEL) for x in all_loggers],
|
||||
# 1
|
||||
[
|
||||
('manticore.manticore', logging.INFO),
|
||||
('manticore.main', logging.INFO),
|
||||
('manticore.ethereum.*', logging.INFO),
|
||||
('manticore.native.*', logging.INFO),
|
||||
('manticore.core.manticore', logging.INFO)
|
||||
("manticore.manticore", logging.INFO),
|
||||
("manticore.main", logging.INFO),
|
||||
("manticore.ethereum.*", logging.INFO),
|
||||
("manticore.native.*", logging.INFO),
|
||||
("manticore.core.manticore", logging.INFO),
|
||||
],
|
||||
# 2 (-v)
|
||||
[
|
||||
('manticore.core.executor', logging.INFO),
|
||||
('manticore.platforms.*', logging.DEBUG),
|
||||
('manticore.ethereum', logging.DEBUG),
|
||||
('manticore.core.plugin', logging.DEBUG),
|
||||
('manticore.util.emulate', logging.INFO),
|
||||
("manticore.core.executor", logging.INFO),
|
||||
("manticore.platforms.*", logging.DEBUG),
|
||||
("manticore.ethereum", logging.DEBUG),
|
||||
("manticore.core.plugin", logging.DEBUG),
|
||||
("manticore.util.emulate", logging.INFO),
|
||||
],
|
||||
# 3 (-vv)
|
||||
[
|
||||
('manticore.native.cpu.*', logging.DEBUG)
|
||||
],
|
||||
[("manticore.native.cpu.*", logging.DEBUG)],
|
||||
# 4 (-vvv)
|
||||
[
|
||||
('manticore.native.memory', logging.DEBUG),
|
||||
('manticore.native.cpu.*', logging.DEBUG),
|
||||
('manticore.native.cpu.*.registers', logging.DEBUG)
|
||||
("manticore.native.memory", logging.DEBUG),
|
||||
("manticore.native.cpu.*", logging.DEBUG),
|
||||
("manticore.native.cpu.*.registers", logging.DEBUG),
|
||||
],
|
||||
# 5 (-vvvv)
|
||||
[
|
||||
('manticore.manticore', logging.DEBUG),
|
||||
('manticore.ethereum.*', logging.DEBUG),
|
||||
('manticore.native.*', logging.DEBUG),
|
||||
('manticore.core.smtlib', logging.DEBUG),
|
||||
('manticore.core.smtlib.*', logging.DEBUG)
|
||||
]
|
||||
("manticore.manticore", logging.DEBUG),
|
||||
("manticore.ethereum.*", logging.DEBUG),
|
||||
("manticore.native.*", logging.DEBUG),
|
||||
("manticore.core.smtlib", logging.DEBUG),
|
||||
("manticore.core.smtlib.*", logging.DEBUG),
|
||||
],
|
||||
]
|
||||
|
||||
|
||||
@@ -127,11 +128,11 @@ def get_verbosity(logger_name):
|
||||
Pseudo globbing that only supports full fields. 'a.*.d' matches 'a.b.d'
|
||||
but not 'a.b.c.d'.
|
||||
"""
|
||||
name_l, pattern_l = name.split('.'), pattern.split('.')
|
||||
name_l, pattern_l = name.split("."), pattern.split(".")
|
||||
if len(name_l) != len(pattern_l):
|
||||
return False
|
||||
for name_f, pattern_f in zip(name_l, pattern_l):
|
||||
if pattern_f == '*':
|
||||
if pattern_f == "*":
|
||||
continue
|
||||
if name_f != pattern_f:
|
||||
return False
|
||||
|
||||
@@ -22,7 +22,7 @@ class WithKeyboardInterruptAs:
|
||||
self.old_handler(sig, frame)
|
||||
else:
|
||||
self.callback()
|
||||
logging.debug('SIGINT received. Supressing KeyboardInterrupt.')
|
||||
logging.debug("SIGINT received. Supressing KeyboardInterrupt.")
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
try:
|
||||
|
||||
@@ -3,11 +3,11 @@ import os
|
||||
import time
|
||||
import threading
|
||||
|
||||
from binaryninja import (
|
||||
PluginCommand, HighlightStandardColor, log, BackgroundTaskThread
|
||||
)
|
||||
from binaryninja import PluginCommand, HighlightStandardColor, log, BackgroundTaskThread
|
||||
from binaryninja.interaction import (
|
||||
get_open_filename_input, get_directory_name_input, get_choice_input
|
||||
get_open_filename_input,
|
||||
get_directory_name_input,
|
||||
get_choice_input,
|
||||
)
|
||||
import binaryninja.enums as enums
|
||||
|
||||
@@ -18,15 +18,19 @@ clear = HighlightStandardColor.NoHighlightColor
|
||||
# renew interval
|
||||
interval = 3
|
||||
|
||||
|
||||
class Singleton(type):
|
||||
_instances = {}
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
||||
return cls._instances[cls]
|
||||
|
||||
|
||||
class TraceVisualizer:
|
||||
__metaclass__ = Singleton
|
||||
|
||||
def __init__(self, view, workspace, base=0x0, live=False):
|
||||
self.view = view
|
||||
self.workspace = workspace
|
||||
@@ -45,11 +49,9 @@ class TraceVisualizer:
|
||||
Given a Manticore workspace, or trace file, highlight the basic blocks.
|
||||
"""
|
||||
if os.path.isfile(self.workspace):
|
||||
t = threading.Thread(target=self.highlight_from_file,
|
||||
args=(self.workspace,))
|
||||
t = threading.Thread(target=self.highlight_from_file, args=(self.workspace,))
|
||||
elif os.path.isdir(self.workspace):
|
||||
t = threading.Thread(target=self.highlight_from_dir,
|
||||
args=(self.workspace,))
|
||||
t = threading.Thread(target=self.highlight_from_dir, args=(self.workspace,))
|
||||
t.start()
|
||||
|
||||
def highlight_from_file(self, tracefile):
|
||||
@@ -62,7 +64,7 @@ class TraceVisualizer:
|
||||
def highlight_from_dir(self, workspace_dir):
|
||||
while True:
|
||||
for f in os.listdir(workspace_dir):
|
||||
if f.endswith('trace'):
|
||||
if f.endswith("trace"):
|
||||
self.process_trace(os.path.join(workspace_dir, f))
|
||||
if not self.live_update:
|
||||
break
|
||||
@@ -104,11 +106,13 @@ class TraceVisualizer:
|
||||
w += " address for function " + str(xref.function)
|
||||
log.log_warn(w)
|
||||
return
|
||||
if not (op == enums.LowLevelILOperation.LLIL_CALL or
|
||||
op == enums.LowLevelILOperation.LLIL_JUMP or
|
||||
op == enums.LowLevelILOperation.LLIL_JUMP_TO or
|
||||
op == enums.LowLevelILOperation.LLIL_SYSCALL or
|
||||
op == enums.LowLevelILOperation.LLIL_GOTO):
|
||||
if not (
|
||||
op == enums.LowLevelILOperation.LLIL_CALL
|
||||
or op == enums.LowLevelILOperation.LLIL_JUMP
|
||||
or op == enums.LowLevelILOperation.LLIL_JUMP_TO
|
||||
or op == enums.LowLevelILOperation.LLIL_SYSCALL
|
||||
or op == enums.LowLevelILOperation.LLIL_GOTO
|
||||
):
|
||||
return
|
||||
self.cov_comments.add((xref.function, xref.address))
|
||||
xref.function.set_comment_at(xref.address, comment)
|
||||
@@ -119,6 +123,7 @@ class TraceVisualizer:
|
||||
for fun, addr in self.cov_comments:
|
||||
fun.set_comment_at(addr, None)
|
||||
|
||||
|
||||
class CoverageHelper(BackgroundTaskThread):
|
||||
def __init__(self, view, tv):
|
||||
self.tv = tv
|
||||
@@ -129,18 +134,18 @@ class CoverageHelper(BackgroundTaskThread):
|
||||
# function cumulative bb coverage
|
||||
# key: function address
|
||||
# values: [total basic blocks covered, xrefs to function]
|
||||
fun_cov = {f.start : [0, 0] for f in self.view.functions}
|
||||
fun_xrefs = sorted([(f, self.view.get_code_refs(f.start))
|
||||
for f in self.view.functions],
|
||||
key=lambda x: len(x[1]))
|
||||
fun_cov = {f.start: [0, 0] for f in self.view.functions}
|
||||
fun_xrefs = sorted(
|
||||
[(f, self.view.get_code_refs(f.start)) for f in self.view.functions],
|
||||
key=lambda x: len(x[1]),
|
||||
)
|
||||
|
||||
for f, xrefs in fun_xrefs:
|
||||
if not f.basic_blocks:
|
||||
continue
|
||||
cov = (len(
|
||||
(set([b.start for b in f.basic_blocks])
|
||||
.intersection(self.tv.cov_bb))
|
||||
) / float(len(set(f.basic_blocks))))
|
||||
cov = len(
|
||||
(set([b.start for b in f.basic_blocks]).intersection(self.tv.cov_bb))
|
||||
) / float(len(set(f.basic_blocks)))
|
||||
fun_cov[f.start][0] += cov
|
||||
for xref_f in xrefs:
|
||||
fun_cov[xref_f.function.start][0] += cov
|
||||
@@ -153,16 +158,16 @@ class CoverageHelper(BackgroundTaskThread):
|
||||
for xref in xrefs:
|
||||
self.tv.set_comment_at_xref(xref, cov)
|
||||
|
||||
|
||||
def get_workspace():
|
||||
choice = get_choice_input("Select Trace Type",
|
||||
"Input",
|
||||
["Trace File", "Manticore Workspace"])
|
||||
choice = get_choice_input("Select Trace Type", "Input", ["Trace File", "Manticore Workspace"])
|
||||
if choice == 0:
|
||||
workspace = get_open_filename_input('Trace File')
|
||||
workspace = get_open_filename_input("Trace File")
|
||||
else:
|
||||
workspace = get_directory_name_input('Workspace Directory')
|
||||
workspace = get_directory_name_input("Workspace Directory")
|
||||
return workspace
|
||||
|
||||
|
||||
def viz_trace(view):
|
||||
"""
|
||||
Given a Manticore trace file, highlight the basic blocks.
|
||||
@@ -172,6 +177,7 @@ def viz_trace(view):
|
||||
tv.workspace = get_workspace()
|
||||
tv.visualize()
|
||||
|
||||
|
||||
def viz_live_trace(view):
|
||||
"""
|
||||
Given a Manticore trace file, highlight the basic blocks.
|
||||
@@ -183,6 +189,7 @@ def viz_live_trace(view):
|
||||
tv.live_update = True
|
||||
tv.visualize()
|
||||
|
||||
|
||||
def get_coverage(view):
|
||||
tv = TraceVisualizer(view, None, live=False)
|
||||
if tv.workspace is None:
|
||||
@@ -191,6 +198,7 @@ def get_coverage(view):
|
||||
c = CoverageHelper(view, tv)
|
||||
c.start()
|
||||
|
||||
|
||||
def clear_all(view):
|
||||
tv = TraceVisualizer(view, None)
|
||||
for addr in tv.highlighted:
|
||||
@@ -199,15 +207,16 @@ def clear_all(view):
|
||||
tv.workspace = None
|
||||
tv.live_update = False
|
||||
|
||||
PluginCommand.register("ManticoreTrace: Highlight",
|
||||
"Highlight Manticore Execution Trace",
|
||||
viz_trace)
|
||||
PluginCommand.register("ManticoreTrace: BB Coverage",
|
||||
"Compute cumulative BB coverage for each function ",
|
||||
get_coverage)
|
||||
PluginCommand.register("ManticoreTrace: Live Highlight",
|
||||
"Highlight Manticore Execution Trace at Real-Time",
|
||||
viz_live_trace)
|
||||
PluginCommand.register("ManticoreTrace: Clear",
|
||||
"Clear Manticore Trace Highlight",
|
||||
clear_all)
|
||||
|
||||
PluginCommand.register(
|
||||
"ManticoreTrace: Highlight", "Highlight Manticore Execution Trace", viz_trace
|
||||
)
|
||||
PluginCommand.register(
|
||||
"ManticoreTrace: BB Coverage", "Compute cumulative BB coverage for each function ", get_coverage
|
||||
)
|
||||
PluginCommand.register(
|
||||
"ManticoreTrace: Live Highlight",
|
||||
"Highlight Manticore Execution Trace at Real-Time",
|
||||
viz_live_trace,
|
||||
)
|
||||
PluginCommand.register("ManticoreTrace: Clear", "Clear Manticore Trace Highlight", clear_all)
|
||||
|
||||
@@ -9,7 +9,9 @@ def compare_traces(dir1: str, dir2: str):
|
||||
|
||||
For now assumes to use `test_xxxxxxx.trace` only.
|
||||
"""
|
||||
get_traces = lambda dir: [f for f in os.listdir(dir) if f.startswith('test_') and f.endswith('.trace')]
|
||||
get_traces = lambda dir: [
|
||||
f for f in os.listdir(dir) if f.startswith("test_") and f.endswith(".trace")
|
||||
]
|
||||
|
||||
traces1 = get_traces(dir1)
|
||||
traces2 = get_traces(dir2)
|
||||
@@ -19,10 +21,10 @@ def compare_traces(dir1: str, dir2: str):
|
||||
|
||||
print("### Comparing traces: ")
|
||||
print(f"dir1 - {dir1} :")
|
||||
print(', '.join(traces1))
|
||||
print(", ".join(traces1))
|
||||
print()
|
||||
print(f"dir2 - {dir2} :")
|
||||
print(', '.join(traces2))
|
||||
print(", ".join(traces2))
|
||||
|
||||
for t1, t2 in zip(traces1, traces2):
|
||||
path1 = os.path.join(dir1, t1)
|
||||
@@ -30,14 +32,14 @@ def compare_traces(dir1: str, dir2: str):
|
||||
|
||||
with open(path1) as fp1, open(path2) as fp2:
|
||||
if fp1.read() != fp2.read():
|
||||
print(f'Files {t1} and {t2} differs.')
|
||||
print(f"Files {t1} and {t2} differs.")
|
||||
else:
|
||||
print(f'Files {t1} and {t2} matches.')
|
||||
print(f"Files {t1} and {t2} matches.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
print(f'Usage: {sys.argv[0]} MCORE_DIR_1 MCORE_DIR_2')
|
||||
print(f"Usage: {sys.argv[0]} MCORE_DIR_1 MCORE_DIR_2")
|
||||
sys.exit()
|
||||
|
||||
dir1, dir2 = sys.argv[1:]
|
||||
@@ -45,7 +47,7 @@ if __name__ == '__main__':
|
||||
not_dir = lambda d: not os.path.isdir(d)
|
||||
|
||||
if not_dir(dir1) or not_dir(dir2):
|
||||
print('One of passed args is not a directory!')
|
||||
print("One of passed args is not a directory!")
|
||||
sys.exit(-1)
|
||||
|
||||
compare_traces(dir1, dir2)
|
||||
|
||||
+39
-39
@@ -20,34 +20,34 @@ import sys
|
||||
import tempfile
|
||||
from urllib.request import urlopen
|
||||
|
||||
BASE_URL = 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/{}?id=refs/tags/v{}'
|
||||
BASE_URL = (
|
||||
"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/{}?id=refs/tags/v{}"
|
||||
)
|
||||
|
||||
# Use an associative list rather than a dict to get deterministic output.
|
||||
ARCH_TABLES = [
|
||||
('i386', 'arch/x86/entry/syscalls/syscall_32.tbl'),
|
||||
('amd64', 'arch/x86/entry/syscalls/syscall_64.tbl'),
|
||||
('armv7', 'arch/arm/tools/syscall.tbl'),
|
||||
("i386", "arch/x86/entry/syscalls/syscall_32.tbl"),
|
||||
("amd64", "arch/x86/entry/syscalls/syscall_64.tbl"),
|
||||
("armv7", "arch/arm/tools/syscall.tbl"),
|
||||
]
|
||||
|
||||
BITSPERLONG_HDR = 'arch/{}/include/uapi/asm/bitsperlong.h'
|
||||
ARCH_UNISTD_HDR = 'arch/{}/include/uapi/asm/unistd.h'
|
||||
UNISTD_HDR = 'include/uapi/asm-generic/unistd.h'
|
||||
BITSPERLONG_HDR = "arch/{}/include/uapi/asm/bitsperlong.h"
|
||||
ARCH_UNISTD_HDR = "arch/{}/include/uapi/asm/unistd.h"
|
||||
UNISTD_HDR = "include/uapi/asm-generic/unistd.h"
|
||||
|
||||
# Format: Manticore arch, Linux arch.
|
||||
# XXX: Code that uses this might need to be tweaked for other architectures to
|
||||
# work properly.
|
||||
UNISTD = [
|
||||
('aarch64', 'arm64')
|
||||
]
|
||||
UNISTD = [("aarch64", "arm64")]
|
||||
|
||||
__ARM_NR_BASE = 0xf0000
|
||||
__ARM_NR_BASE = 0xF0000
|
||||
ADDITIONAL_SYSCALLS = {
|
||||
'armv7': [
|
||||
('sys_ARM_NR_breakpoint', __ARM_NR_BASE + 1),
|
||||
('sys_ARM_NR_cacheflush', __ARM_NR_BASE + 2),
|
||||
('sys_ARM_NR_usr26', __ARM_NR_BASE + 3),
|
||||
('sys_ARM_NR_usr32', __ARM_NR_BASE + 4),
|
||||
('sys_ARM_NR_set_tls', __ARM_NR_BASE + 5),
|
||||
"armv7": [
|
||||
("sys_ARM_NR_breakpoint", __ARM_NR_BASE + 1),
|
||||
("sys_ARM_NR_cacheflush", __ARM_NR_BASE + 2),
|
||||
("sys_ARM_NR_usr26", __ARM_NR_BASE + 3),
|
||||
("sys_ARM_NR_usr32", __ARM_NR_BASE + 4),
|
||||
("sys_ARM_NR_set_tls", __ARM_NR_BASE + 5),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -66,29 +66,31 @@ def write_without_includes(f, res):
|
||||
line = line.decode()
|
||||
line = line.strip()
|
||||
if not line.startswith("#include"):
|
||||
f.write(line + '\n')
|
||||
f.write(line + "\n")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Generate syscall tables')
|
||||
parser.add_argument('output', help='Python output to generate tables')
|
||||
parser.add_argument('--linux_version', help='Major version of the Linux kernel to use', default='4.11')
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Generate syscall tables")
|
||||
parser.add_argument("output", help="Python output to generate tables")
|
||||
parser.add_argument(
|
||||
"--linux_version", help="Major version of the Linux kernel to use", default="4.11"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
output = open(args.output, 'w+')
|
||||
output.write('#\n#\n# AUTOGENERATED, DO NOT EDIT\n#\n')
|
||||
output.write(f'# From version: {args.linux_version}\n#\n\n')
|
||||
output = open(args.output, "w+")
|
||||
output.write("#\n#\n# AUTOGENERATED, DO NOT EDIT\n#\n")
|
||||
output.write(f"# From version: {args.linux_version}\n#\n\n")
|
||||
|
||||
for arch, path in ARCH_TABLES:
|
||||
url = BASE_URL.format(path, args.linux_version)
|
||||
res = open_url(url)
|
||||
|
||||
output.write(f'{arch} = {{\n')
|
||||
output.write(f"{arch} = {{\n")
|
||||
|
||||
for line in res.readlines():
|
||||
line = line.decode()
|
||||
line = line.strip()
|
||||
if line.startswith('#'):
|
||||
if line.startswith("#"):
|
||||
continue
|
||||
columns = line.split()
|
||||
if len(columns) < 4:
|
||||
@@ -99,7 +101,7 @@ if __name__ == '__main__':
|
||||
for entry, num in ADDITIONAL_SYSCALLS.get(arch, {}):
|
||||
output.write(f' {num}: "{entry}",\n')
|
||||
|
||||
output.write('}\n')
|
||||
output.write("}\n")
|
||||
|
||||
for march, larch in UNISTD:
|
||||
bitsperlong_hdr = BITSPERLONG_HDR.format(larch)
|
||||
@@ -113,33 +115,31 @@ if __name__ == '__main__':
|
||||
arch_unistd_res = open_url(arch_unistd_url)
|
||||
unistd_res = open_url(unistd_url)
|
||||
|
||||
syscall_rx = 'SYSCALL: (\d+) ([a-z_0-9]+)'
|
||||
syscall_define = '#define __SYSCALL(nr, sym) SYSCALL: nr sym'
|
||||
syscall_rx = "SYSCALL: (\d+) ([a-z_0-9]+)"
|
||||
syscall_define = "#define __SYSCALL(nr, sym) SYSCALL: nr sym"
|
||||
|
||||
output.write(f'{march} = {{\n')
|
||||
output.write(f"{march} = {{\n")
|
||||
|
||||
fd, tmp_path = tempfile.mkstemp()
|
||||
try:
|
||||
with os.fdopen(fd, 'w') as tmp:
|
||||
with os.fdopen(fd, "w") as tmp:
|
||||
# The order is important here for CPP to work correctly.
|
||||
tmp.write(syscall_define + '\n')
|
||||
tmp.write(syscall_define + "\n")
|
||||
write_without_includes(tmp, bitsperlong_res)
|
||||
write_without_includes(tmp, arch_unistd_res)
|
||||
write_without_includes(tmp, unistd_res)
|
||||
|
||||
process = subprocess.Popen(
|
||||
['cpp', '-E', tmp_path],
|
||||
stdout=subprocess.PIPE,
|
||||
encoding='utf-8'
|
||||
["cpp", "-E", tmp_path], stdout=subprocess.PIPE, encoding="utf-8"
|
||||
)
|
||||
out, _ = process.communicate()
|
||||
lines = out.split('\n')
|
||||
lines = out.split("\n")
|
||||
for line in lines:
|
||||
m = re.search(syscall_rx, line)
|
||||
if m:
|
||||
num = m.group(1)
|
||||
entry = m.group(2)
|
||||
if entry != 'sys_ni_syscall': # not implemented syscall
|
||||
if entry != "sys_ni_syscall": # not implemented syscall
|
||||
output.write(f' {num}: "{entry}",\n')
|
||||
finally:
|
||||
os.remove(tmp_path)
|
||||
@@ -147,4 +147,4 @@ if __name__ == '__main__':
|
||||
for entry, num in ADDITIONAL_SYSCALLS.get(march, {}):
|
||||
output.write(f' {num}: "{entry}",\n')
|
||||
|
||||
output.write('}\n')
|
||||
output.write("}\n")
|
||||
|
||||
+32
-19
@@ -33,9 +33,11 @@ class TraceReceiver(Plugin):
|
||||
def will_generate_testcase_callback(self, state, testcase, msg):
|
||||
self._trace = state.context[self._tracer.context_key]
|
||||
|
||||
instructions, writes = _partition(lambda x: x['type'] == 'regs', self._trace)
|
||||
instructions, writes = _partition(lambda x: x["type"] == "regs", self._trace)
|
||||
total = len(self._trace)
|
||||
print(f'Recorded concrete trace: {len(instructions)}/{total} instructions, {len(writes)}/{total} writes')
|
||||
print(
|
||||
f"Recorded concrete trace: {len(instructions)}/{total} instructions, {len(writes)}/{total} writes"
|
||||
)
|
||||
|
||||
|
||||
class Follower(Plugin):
|
||||
@@ -48,32 +50,32 @@ class Follower(Plugin):
|
||||
super().__init__()
|
||||
|
||||
def add_symbolic_range(self, pc_start, pc_end):
|
||||
self.symbolic_ranges.append((pc_start,pc_end))
|
||||
self.symbolic_ranges.append((pc_start, pc_end))
|
||||
|
||||
def get_next(self, type):
|
||||
event = self.trace[self.index]
|
||||
assert event['type'] == type
|
||||
assert event["type"] == type
|
||||
self.index += 1
|
||||
return event
|
||||
|
||||
def did_write_memory_callback(self, state, where, value, size):
|
||||
if not self.active:
|
||||
return
|
||||
write = self.get_next('mem_write')
|
||||
write = self.get_next("mem_write")
|
||||
|
||||
if not issymbolic(value):
|
||||
return
|
||||
|
||||
assert write['where'] == where and write['size'] == size
|
||||
state.constrain(value == write['value'])
|
||||
assert write["where"] == where and write["size"] == size
|
||||
state.constrain(value == write["value"])
|
||||
|
||||
def did_execute_instruction_callback(self, state, last_pc, pc, insn):
|
||||
if not self.active:
|
||||
return
|
||||
event = self.get_next('regs')
|
||||
self.last_instruction = event['values']
|
||||
event = self.get_next("regs")
|
||||
self.last_instruction = event["values"]
|
||||
if issymbolic(pc):
|
||||
state.constrain(state.cpu.RIP == self.last_instruction['RIP'])
|
||||
state.constrain(state.cpu.RIP == self.last_instruction["RIP"])
|
||||
else:
|
||||
for start, stop in self.symbolic_ranges:
|
||||
if start <= pc <= stop:
|
||||
@@ -81,13 +83,24 @@ class Follower(Plugin):
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Follow a concrete trace')
|
||||
parser.add_argument('-f', '--explore_from', help='Value of PC from which to explore symbolically', type=str)
|
||||
parser.add_argument('-t', '--explore_to', type=str, default=sys.maxsize,
|
||||
help="Value of PC until which to explore symbolically. (Probably don't want this set)")
|
||||
parser.add_argument('--verbose', '-v', action='count', default=0, help='Increase verbosity')
|
||||
parser.add_argument('cmd', type=str, nargs='+',
|
||||
help='Program and arguments. Use "--" to separate script arguments from target arguments')
|
||||
parser = argparse.ArgumentParser(description="Follow a concrete trace")
|
||||
parser.add_argument(
|
||||
"-f", "--explore_from", help="Value of PC from which to explore symbolically", type=str
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--explore_to",
|
||||
type=str,
|
||||
default=sys.maxsize,
|
||||
help="Value of PC until which to explore symbolically. (Probably don't want this set)",
|
||||
)
|
||||
parser.add_argument("--verbose", "-v", action="count", default=0, help="Increase verbosity")
|
||||
parser.add_argument(
|
||||
"cmd",
|
||||
type=str,
|
||||
nargs="+",
|
||||
help='Program and arguments. Use "--" to separate script arguments from target arguments',
|
||||
)
|
||||
args = parser.parse_args(sys.argv[1:])
|
||||
|
||||
range = None
|
||||
@@ -106,7 +119,7 @@ def main():
|
||||
time.sleep(3)
|
||||
|
||||
# Create a symbolic Manticore and follow last trace
|
||||
symbolic_args = ['+'*len(arg) for arg in args.cmd[1:]]
|
||||
symbolic_args = ["+" * len(arg) for arg in args.cmd[1:]]
|
||||
m2 = Manticore.linux(args.cmd[0], symbolic_args)
|
||||
f = Follower(r.trace)
|
||||
if range:
|
||||
@@ -116,5 +129,5 @@ def main():
|
||||
m2.run()
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
+35
-38
@@ -2,37 +2,36 @@ import subprocess
|
||||
|
||||
count = 0
|
||||
|
||||
prompt = ''
|
||||
prompt = ""
|
||||
subproc = None
|
||||
_arch = None
|
||||
|
||||
|
||||
def drain():
|
||||
str_buffer = ''
|
||||
str_buffer = ""
|
||||
while not str_buffer.endswith(prompt):
|
||||
c = subproc.stdout.read(1)
|
||||
str_buffer += c
|
||||
return str_buffer[:-len(prompt)]
|
||||
return str_buffer[: -len(prompt)]
|
||||
|
||||
|
||||
def start(arch, argv, port=1234, _prompt='(gdb) '):
|
||||
def start(arch, argv, port=1234, _prompt="(gdb) "):
|
||||
global prompt, subproc
|
||||
prompt = _prompt
|
||||
gdb = 'gdb-multiarch'
|
||||
gdb = "gdb-multiarch"
|
||||
try:
|
||||
subproc = subprocess.Popen([gdb, argv[0]],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
subproc = subprocess.Popen(
|
||||
[gdb, argv[0]], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
except OSError:
|
||||
msg = f"'{gdb}' binary not found in PATH (needed for tracing)"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
drain()
|
||||
# correspond(f'set architecture {arch}\n')
|
||||
correspond(f'file {argv[0]}\n')
|
||||
correspond(f'target remote :{port}\n')
|
||||
correspond('set pagination off\n')
|
||||
correspond(f"file {argv[0]}\n")
|
||||
correspond(f"target remote :{port}\n")
|
||||
correspond("set pagination off\n")
|
||||
|
||||
|
||||
def correspond(text):
|
||||
@@ -43,38 +42,38 @@ def correspond(text):
|
||||
|
||||
|
||||
def getInstruction():
|
||||
return correspond('x/i $pc\n').split('\n')[0]
|
||||
return correspond("x/i $pc\n").split("\n")[0]
|
||||
|
||||
|
||||
def getR(reg):
|
||||
reg = "$" + reg
|
||||
if "XMM" in reg:
|
||||
reg = reg + ".uint128"
|
||||
val = correspond(f'p {reg.lower()}\n').split("=")[-1].split("\n")[0]
|
||||
val = correspond(f"p {reg.lower()}\n").split("=")[-1].split("\n")[0]
|
||||
if "0x" in val:
|
||||
return int(val.split("0x")[-1], 16)
|
||||
else:
|
||||
return int(val)
|
||||
if "FLAG" in reg:
|
||||
reg = "(unsigned) " + reg
|
||||
if reg in [f'$R{i}B' for i in range(16)]:
|
||||
if reg in [f"$R{i}B" for i in range(16)]:
|
||||
reg = reg[:-1] + "&0xff"
|
||||
if reg in [f'$R{i}W' for i in range(16)]:
|
||||
if reg in [f"$R{i}W" for i in range(16)]:
|
||||
reg = reg[:-1] + "&0xffff"
|
||||
val = correspond(f'p /x {reg.lower()}\n')
|
||||
val = correspond(f"p /x {reg.lower()}\n")
|
||||
val = val.split("0x")[-1]
|
||||
return int(val.split("\n")[0], 16)
|
||||
|
||||
|
||||
def getCanonicalRegisters():
|
||||
reg_output = correspond('info reg\n')
|
||||
reg_output = correspond("info reg\n")
|
||||
registers = {}
|
||||
for line in reg_output.split("\n"):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
name, hex_val = line.split()[:2]
|
||||
if name != 'cpsr':
|
||||
if name != "cpsr":
|
||||
registers[name] = int(hex_val, 0)
|
||||
else:
|
||||
# We just want the NZCV flags
|
||||
@@ -83,7 +82,7 @@ def getCanonicalRegisters():
|
||||
|
||||
|
||||
def setR(reg, value):
|
||||
correspond(f'set ${reg.lower()} = {int(value)}\n')
|
||||
correspond(f"set ${reg.lower()} = {int(value)}\n")
|
||||
|
||||
|
||||
def stepi():
|
||||
@@ -93,52 +92,50 @@ def stepi():
|
||||
|
||||
def getM(m):
|
||||
try:
|
||||
return int(correspond(f'x/xg {m}\n').strip().split('\t')[-1], 0)
|
||||
return int(correspond(f"x/xg {m}\n").strip().split("\t")[-1], 0)
|
||||
except Exception as e:
|
||||
raise e
|
||||
return 0
|
||||
|
||||
|
||||
def getPid():
|
||||
return int(correspond('info proc\n').split("\n")[0].split(" ")[-1])
|
||||
return int(correspond("info proc\n").split("\n")[0].split(" ")[-1])
|
||||
|
||||
|
||||
def getStack():
|
||||
p = correspond('info proc\n').split('\n')[0].split(' ')[-1]
|
||||
p = correspond("info proc\n").split("\n")[0].split(" ")[-1]
|
||||
with open(f"/proc/{p}/maps") as f:
|
||||
maps = f.read().split("\n")
|
||||
i, o = [int(x, 16) for x in maps[-3].split(" ")[0].split('-')]
|
||||
i, o = [int(x, 16) for x in maps[-3].split(" ")[0].split("-")]
|
||||
|
||||
|
||||
def setByte(addr, val):
|
||||
cmdstr = f'set {{char}}{addr} = {ord(val)}'
|
||||
correspond(cmdstr + '\n')
|
||||
cmdstr = f"set {{char}}{addr} = {ord(val)}"
|
||||
correspond(cmdstr + "\n")
|
||||
|
||||
|
||||
def getByte(m):
|
||||
arch = get_arch()
|
||||
mask = {'i386': 0xffffffff,
|
||||
'armv7': 0xffffffff,
|
||||
'amd64': 0xffffffffffffffff}[arch]
|
||||
mask = {"i386": 0xFFFFFFFF, "armv7": 0xFFFFFFFF, "amd64": 0xFFFFFFFFFFFFFFFF}[arch]
|
||||
return int(correspond(f"x/1bx {m & mask}\n").split("\t")[-1].split("\n")[0][2:], 16)
|
||||
|
||||
|
||||
def get_entry():
|
||||
a = correspond('info target\n')
|
||||
return int(a[a.find("Entry point:"):].split('\n')[0].split(' ')[-1][2:], 16)
|
||||
a = correspond("info target\n")
|
||||
return int(a[a.find("Entry point:") :].split("\n")[0].split(" ")[-1][2:], 16)
|
||||
|
||||
|
||||
def get_arch():
|
||||
global _arch
|
||||
if _arch is not None:
|
||||
return _arch
|
||||
infotarget = correspond('info target\n')
|
||||
if 'elf32-i386' in infotarget:
|
||||
_arch = 'i386'
|
||||
elif 'elf64-x86-64' in infotarget:
|
||||
_arch = 'amd64'
|
||||
elif 'elf32-littlearm' in infotarget:
|
||||
_arch = 'armv7'
|
||||
infotarget = correspond("info target\n")
|
||||
if "elf32-i386" in infotarget:
|
||||
_arch = "i386"
|
||||
elif "elf64-x86-64" in infotarget:
|
||||
_arch = "amd64"
|
||||
elif "elf32-littlearm" in infotarget:
|
||||
_arch = "armv7"
|
||||
else:
|
||||
print(infotarget)
|
||||
raise NotImplemented
|
||||
|
||||
+7
-5
@@ -9,14 +9,14 @@ from sys import argv, exit
|
||||
from manticore.native import Manticore
|
||||
|
||||
|
||||
def profile(program, sort='cumulative'):
|
||||
print(f"[*] Profiling program \"{program}\"")
|
||||
def profile(program, sort="cumulative"):
|
||||
print(f'[*] Profiling program "{program}"')
|
||||
|
||||
m = Manticore(program)
|
||||
m.run(should_profile=True)
|
||||
|
||||
stats = m.get_profiling_stats()
|
||||
print(f'[*] Loaded profiling data.')
|
||||
print(f"[*] Loaded profiling data.")
|
||||
|
||||
if stats is None:
|
||||
print(f"[*] Failed to collect stats for program {program}")
|
||||
@@ -30,8 +30,10 @@ def profile(program, sort='cumulative'):
|
||||
if __name__ == "__main__":
|
||||
if len(argv) != 2:
|
||||
print(f"usage: python {argv[0]} PROGRAM [SORT_METHOD]")
|
||||
print('The default SORT_METHOD is cumulative')
|
||||
print('SORT_METHODs can be seen on https://docs.python.org/3/library/profile.html#pstats.Stats.sort_stats')
|
||||
print("The default SORT_METHOD is cumulative")
|
||||
print(
|
||||
"SORT_METHODs can be seen on https://docs.python.org/3/library/profile.html#pstats.Stats.sort_stats"
|
||||
)
|
||||
exit()
|
||||
|
||||
profile(argv[1])
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
for f in sys.stdin.readlines():
|
||||
line = f.strip()
|
||||
if line.endswith(".py") and os.path.exists(line):
|
||||
print(line)
|
||||
+23
-26
@@ -16,40 +16,39 @@ subproc = None
|
||||
stats = None
|
||||
_arch = None
|
||||
|
||||
|
||||
def get_lines(n=1):
|
||||
lines = []
|
||||
str_buffer = ''
|
||||
str_buffer = ""
|
||||
received_lines = 0
|
||||
while received_lines < n:
|
||||
c = subproc.stdout.read(1)
|
||||
str_buffer += c
|
||||
if c == '\n':
|
||||
if c == "\n":
|
||||
lines.append(str_buffer)
|
||||
str_buffer = ''
|
||||
str_buffer = ""
|
||||
received_lines += 1
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
def parse_mmu_debug_output(s):
|
||||
d = {}
|
||||
|
||||
# Get guest address space
|
||||
d['reserved'] = int(s.pop(0).split()[1], 0)
|
||||
d['host_mmap_min_addr'] = int(s.pop(0).split('=')[1], 0)
|
||||
d['guest_base'] = int(s.pop(0).split()[1], 0)
|
||||
d["reserved"] = int(s.pop(0).split()[1], 0)
|
||||
d["host_mmap_min_addr"] = int(s.pop(0).split("=")[1], 0)
|
||||
d["guest_base"] = int(s.pop(0).split()[1], 0)
|
||||
|
||||
# get rid of mapping heading
|
||||
s.pop(0)
|
||||
d['maps'] = []
|
||||
|
||||
while '-' in s[0]:
|
||||
d["maps"] = []
|
||||
|
||||
while "-" in s[0]:
|
||||
line = s.pop(0)
|
||||
range, size, protections = line.split()
|
||||
start, end = range.split('-')
|
||||
d['maps'].append((int(start, 16),
|
||||
int(end, 16),
|
||||
int(size, 16),
|
||||
protections))
|
||||
start, end = range.split("-")
|
||||
d["maps"].append((int(start, 16), int(end, 16), int(size, 16), protections))
|
||||
|
||||
while s:
|
||||
line = s.pop(0)
|
||||
@@ -61,32 +60,30 @@ def parse_mmu_debug_output(s):
|
||||
return d
|
||||
|
||||
|
||||
def start(arch, argv, port=1234, va_size=0xc0000000, stack_size=0x20000):
|
||||
def start(arch, argv, port=1234, va_size=0xC0000000, stack_size=0x20000):
|
||||
global subproc, stats
|
||||
aslr_file = '/proc/sys/kernel/randomize_va_space'
|
||||
aslr_file = "/proc/sys/kernel/randomize_va_space"
|
||||
try:
|
||||
with open(aslr_file, 'r') as f:
|
||||
if f.read().strip() != '0':
|
||||
with open(aslr_file, "r") as f:
|
||||
if f.read().strip() != "0":
|
||||
logger.warning("Disable ASLR before running qemu-user")
|
||||
logger.warning(f" sudo sh -c 'echo 0 > {aslr_file}'")
|
||||
finally:
|
||||
pass
|
||||
|
||||
args = [f'qemu-{arch}', '-g', port, '-d', 'mmu', '-R', va_size, '-s', stack_size] + argv
|
||||
|
||||
args = [f"qemu-{arch}", "-g", port, "-d", "mmu", "-R", va_size, "-s", stack_size] + argv
|
||||
args = map(str, args)
|
||||
print(f"Running: {' '.join(args)}")
|
||||
subproc = subprocess.Popen(
|
||||
args,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT
|
||||
args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
mmu_debug_output = get_lines(16)
|
||||
|
||||
stats = parse_mmu_debug_output(mmu_debug_output)
|
||||
for m in stats['maps']:
|
||||
for m in stats["maps"]:
|
||||
start, end, size, perms = m
|
||||
print(f'{start:x}-{end:x}, {size}, {perms}')
|
||||
print(f"{start:x}-{end:x}, {size}, {perms}")
|
||||
|
||||
|
||||
def correspond(text):
|
||||
"""Communicate with the child process without closing stdin."""
|
||||
|
||||
@@ -42,9 +42,9 @@ def binary_arch(binary):
|
||||
:rtype bool: True for x86_64, False otherwise
|
||||
"""
|
||||
|
||||
with open(binary, 'rb') as f:
|
||||
with open(binary, "rb") as f:
|
||||
elffile = ELFFile(f)
|
||||
if elffile['e_machine'] == 'EM_X86_64':
|
||||
if elffile["e_machine"] == "EM_X86_64":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -63,8 +63,7 @@ def binary_symbols(binary):
|
||||
def substr_after(string, delim):
|
||||
return string.partition(delim)[2]
|
||||
|
||||
|
||||
with open(binary, 'rb') as f:
|
||||
with open(binary, "rb") as f:
|
||||
elffile = ELFFile(f)
|
||||
|
||||
for section in elffile.iter_sections():
|
||||
@@ -72,8 +71,9 @@ def binary_symbols(binary):
|
||||
continue
|
||||
|
||||
symbols = [sym.name for sym in section.iter_symbols() if sym]
|
||||
return [substr_after(name, PREPEND_SYM) for name in symbols
|
||||
if name.startswith(PREPEND_SYM)]
|
||||
return [
|
||||
substr_after(name, PREPEND_SYM) for name in symbols if name.startswith(PREPEND_SYM)
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
@@ -81,22 +81,43 @@ def main():
|
||||
|
||||
# required arg group for help display
|
||||
required = parser.add_argument_group("required arguments")
|
||||
required.add_argument("-t", "--test", dest="test", required=True,
|
||||
help="Target binary for sandshrew analysis")
|
||||
required.add_argument(
|
||||
"-t", "--test", dest="test", required=True, help="Target binary for sandshrew analysis"
|
||||
)
|
||||
|
||||
# constraint configuration
|
||||
parser.add_argument("-c", "--constraint", dest="constraint", required=False,
|
||||
help="Constraint to apply to symbolic input. Includes ascii, alpha, num, or alphanum")
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--constraint",
|
||||
dest="constraint",
|
||||
required=False,
|
||||
help="Constraint to apply to symbolic input. Includes ascii, alpha, num, or alphanum",
|
||||
)
|
||||
|
||||
# debugging options
|
||||
parser.add_argument("--debug", dest="debug", action="store_true", required=False,
|
||||
help="If set, turns on debugging output for sandshrew")
|
||||
parser.add_argument("--trace", dest="trace", action="store_true", required=False,
|
||||
help="If set, trace instruction recording will be outputted to logger")
|
||||
parser.add_argument(
|
||||
"--debug",
|
||||
dest="debug",
|
||||
action="store_true",
|
||||
required=False,
|
||||
help="If set, turns on debugging output for sandshrew",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--trace",
|
||||
dest="trace",
|
||||
action="store_true",
|
||||
required=False,
|
||||
help="If set, trace instruction recording will be outputted to logger",
|
||||
)
|
||||
|
||||
# other configuration settings
|
||||
parser.add_argument("--cmpsym", dest="cmp_sym", default="__strcmp_ssse3", required=False,
|
||||
help="Overrides comparison function used to test for equivalence (default is strcmp)")
|
||||
parser.add_argument(
|
||||
"--cmpsym",
|
||||
dest="cmp_sym",
|
||||
default="__strcmp_ssse3",
|
||||
required=False,
|
||||
help="Overrides comparison function used to test for equivalence (default is strcmp)",
|
||||
)
|
||||
|
||||
# parse or print help
|
||||
args = parser.parse_args()
|
||||
@@ -113,18 +134,18 @@ def main():
|
||||
raise NotImplementedError("sandshrew only supports x86_64 binary concretization")
|
||||
|
||||
# initialize Manticore
|
||||
m = Manticore.linux(args.test, ['+' * BUFFER_SIZE])
|
||||
m = Manticore.linux(args.test, ["+" * BUFFER_SIZE])
|
||||
m.verbosity(2)
|
||||
|
||||
# initialize mcore context manager
|
||||
m.context['syms'] = binary_symbols(args.test)
|
||||
m.context['exec_flag'] = False
|
||||
m.context['argv1'] = None
|
||||
m.context["syms"] = binary_symbols(args.test)
|
||||
m.context["exec_flag"] = False
|
||||
m.context["argv1"] = None
|
||||
|
||||
logging.debug(f"Functions for concretization: {m.context['syms']}")
|
||||
|
||||
# add record trace hook throughout execution
|
||||
m.context['trace'] = []
|
||||
m.context["trace"] = []
|
||||
|
||||
# initialize state by checking and storing symbolic argv
|
||||
@m.init
|
||||
@@ -133,14 +154,13 @@ def main():
|
||||
logging.debug(f"Checking for symbolic ARGV")
|
||||
|
||||
# determine argv[1] from state.input_symbols by label name
|
||||
argv1 = next(sym for sym in next(ready_states).input_symbols if sym.name == 'ARGV1')
|
||||
argv1 = next(sym for sym in next(ready_states).input_symbols if sym.name == "ARGV1")
|
||||
if argv1 is None:
|
||||
raise RuntimeException("ARGV was not provided and/or made symbolic")
|
||||
|
||||
# store argv1 in global state
|
||||
with m.locked_context() as context:
|
||||
context['argv1'] = argv1
|
||||
|
||||
context["argv1"] = argv1
|
||||
|
||||
# store a trace counter, and output if arg was set
|
||||
@m.hook(None)
|
||||
@@ -149,10 +169,9 @@ def main():
|
||||
if args.trace:
|
||||
print(f"{hex(pc)}")
|
||||
with m.locked_context() as context:
|
||||
context['trace'] += [pc]
|
||||
context["trace"] += [pc]
|
||||
|
||||
|
||||
for sym in m.context['syms']:
|
||||
for sym in m.context["syms"]:
|
||||
|
||||
@m.hook(m.resolve("SANDSHREW_" + sym))
|
||||
def concrete_checker(state):
|
||||
@@ -171,7 +190,7 @@ def main():
|
||||
logging.debug(f"Symbolic input parameter to function {sym}() detected")
|
||||
|
||||
# store instruction after `call SANDSHREW_*`
|
||||
return_pc = context['trace'][-1] + 5
|
||||
return_pc = context["trace"][-1] + 5
|
||||
|
||||
# attach a hook to the return_pc, as this is where we will perform concolic execution
|
||||
@m.hook(return_pc)
|
||||
@@ -182,7 +201,7 @@ def main():
|
||||
with m.locked_context() as context:
|
||||
|
||||
# output param is RDI, symbolicate RAX
|
||||
context['return_addr'] = cpu.RAX
|
||||
context["return_addr"] = cpu.RAX
|
||||
logging.debug(f"Writing unconstrained buffer to output memory location")
|
||||
|
||||
# initialize unconstrained symbolic input
|
||||
@@ -192,23 +211,38 @@ def main():
|
||||
for i in range(BUFFER_SIZE):
|
||||
|
||||
if args.constraint == "alpha":
|
||||
state.constrain(operators.OR(
|
||||
operators.AND(ord('A') <= return_buf[i], return_buf[i] <= ord('Z')),
|
||||
operators.AND(ord('a') <= return_buf[i], return_buf[i] <= ord('z'))
|
||||
))
|
||||
state.constrain(
|
||||
operators.OR(
|
||||
operators.AND(
|
||||
ord("A") <= return_buf[i], return_buf[i] <= ord("Z")
|
||||
),
|
||||
operators.AND(
|
||||
ord("a") <= return_buf[i], return_buf[i] <= ord("z")
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
elif args.constraint == "num":
|
||||
state.constrain(operators.AND(ord('0') <= return_buf[i], return_buf[i] <= ord('9')))
|
||||
state.constrain(
|
||||
operators.AND(
|
||||
ord("0") <= return_buf[i], return_buf[i] <= ord("9")
|
||||
)
|
||||
)
|
||||
|
||||
elif args.constraint == "alphanum":
|
||||
raise NotImplementedError("alphanum constraint set not yet implemented")
|
||||
raise NotImplementedError(
|
||||
"alphanum constraint set not yet implemented"
|
||||
)
|
||||
|
||||
elif args.constraint == "ascii":
|
||||
state.constrain(operators.AND(ord(' ') <= return_buf[i], return_buf[i] <= ord('}')))
|
||||
state.constrain(
|
||||
operators.AND(
|
||||
ord(" ") <= return_buf[i], return_buf[i] <= ord("}")
|
||||
)
|
||||
)
|
||||
|
||||
# write to address
|
||||
state.cpu.write_bytes(context['return_addr'], return_buf)
|
||||
|
||||
state.cpu.write_bytes(context["return_addr"], return_buf)
|
||||
|
||||
@m.hook(m.resolve(sym))
|
||||
def concolic_hook(state):
|
||||
@@ -220,7 +254,7 @@ def main():
|
||||
with m.locked_context() as context:
|
||||
|
||||
# store `call sym` instruction and ret val instruction
|
||||
call_pc = context['trace'][-1]
|
||||
call_pc = context["trace"][-1]
|
||||
|
||||
# we are currently in the function prologue of `sym`. Let's go back to `call sym`.
|
||||
state.cpu.PC = call_pc
|
||||
@@ -233,7 +267,6 @@ def main():
|
||||
|
||||
logging.debug("Continuing with Manticore symbolic execution")
|
||||
|
||||
|
||||
# TODO(alan): resolve ifunc (different archs use different optimized implementations)
|
||||
@m.hook(m.resolve(args.cmp_sym))
|
||||
def cmp_model(state):
|
||||
@@ -245,8 +278,7 @@ def main():
|
||||
logging.debug("Invoking model for comparsion call")
|
||||
state.invoke_model(strcmp)
|
||||
|
||||
|
||||
@m.hook(m.resolve('abort'))
|
||||
@m.hook(m.resolve("abort"))
|
||||
def fail_state(state):
|
||||
"""
|
||||
hook attached at fail state signified by abort call, which indicates that an edge case
|
||||
@@ -257,20 +289,21 @@ def main():
|
||||
|
||||
# solve for the symbolic argv input
|
||||
with m.locked_context() as context:
|
||||
solution = state.solve_one(context['return_addr'], BUFFER_SIZE)
|
||||
solution = state.solve_one(context["return_addr"], BUFFER_SIZE)
|
||||
print(f"Solution found: {solution}")
|
||||
|
||||
# write solution to individual test case to workspace
|
||||
rand_str = lambda n: ''.join([random.choice(string.ascii_lowercase) for i in range(n)])
|
||||
with open(m.workspace + '/' + 'sandshrew_' + rand_str(4), 'w') as fd:
|
||||
rand_str = lambda n: "".join([random.choice(string.ascii_lowercase) for i in range(n)])
|
||||
with open(m.workspace + "/" + "sandshrew_" + rand_str(4), "w") as fd:
|
||||
fd.write(str(solution))
|
||||
|
||||
m.terminate()
|
||||
|
||||
|
||||
# run manticore
|
||||
m.run()
|
||||
print(f"Total instructions: {len(m.context['trace'])}\nLast instruction: {hex(m.context['trace'][-1])}")
|
||||
print(
|
||||
f"Total instructions: {len(m.context['trace'])}\nLast instruction: {hex(m.context['trace'][-1])}"
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
+39
-23
@@ -3,24 +3,36 @@ import sys, os, pickle, argparse
|
||||
|
||||
# Process the data of a workspace and prints out some global information
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
################################################################################
|
||||
# parse arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Extract different statistic and information from a manticore workspace')
|
||||
parser.add_argument('--workspace', type=str, default=None,
|
||||
help='A folder name for temporaries and results. (default mcore_?????)')
|
||||
parser.add_argument('--pcfreq', action='store_true', help='Print out visited pc and frequency')
|
||||
parser.add_argument('--visited', action='store_true', help='Print out visited pc set')
|
||||
parser.add_argument('--bbs', action='store_true', help='Print out visited basic blocks ')
|
||||
description="Extract different statistic and information from a manticore workspace"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--workspace",
|
||||
type=str,
|
||||
default=None,
|
||||
help="A folder name for temporaries and results. (default mcore_?????)",
|
||||
)
|
||||
parser.add_argument("--pcfreq", action="store_true", help="Print out visited pc and frequency")
|
||||
parser.add_argument("--visited", action="store_true", help="Print out visited pc set")
|
||||
parser.add_argument("--bbs", action="store_true", help="Print out visited basic blocks ")
|
||||
|
||||
parser.add_argument('workspace', type=str, nargs=1, metavar='WORKSPACE',
|
||||
help='The folder name for temporaries and results')
|
||||
parser.add_argument(
|
||||
"workspace",
|
||||
type=str,
|
||||
nargs=1,
|
||||
metavar="WORKSPACE",
|
||||
help="The folder name for temporaries and results",
|
||||
)
|
||||
parsed = parser.parse_args(sys.argv[1:])
|
||||
parsed.workspace = parsed.workspace[0]
|
||||
|
||||
assert int(parsed.pcfreq) + int(parsed.visited) + int(
|
||||
parsed.bbs) == 1, "Choose one option from: --pcfreq --visited"
|
||||
assert (
|
||||
int(parsed.pcfreq) + int(parsed.visited) + int(parsed.bbs) == 1
|
||||
), "Choose one option from: --pcfreq --visited"
|
||||
return parsed
|
||||
|
||||
|
||||
@@ -28,40 +40,44 @@ args = parse_arguments()
|
||||
workspace = args.workspace
|
||||
|
||||
# search previously generated states
|
||||
saved_states = [os.path.join(workspace, filename) for filename in os.listdir(workspace) if filename.endswith('.pkl')]
|
||||
saved_states = [
|
||||
os.path.join(workspace, filename)
|
||||
for filename in os.listdir(workspace)
|
||||
if filename.endswith(".pkl")
|
||||
]
|
||||
# prepare a dictionary to hold states
|
||||
db = {}
|
||||
edges = {}
|
||||
for filename in saved_states:
|
||||
try:
|
||||
with open(filename, 'rb') as f:
|
||||
with open(filename, "rb") as f:
|
||||
# load the whole saved state (memory/cpu/solver/..)
|
||||
state = pickle.loads(f.read())
|
||||
lastpc = 'ROOT'
|
||||
lastpc = "ROOT"
|
||||
for proc, pc in state.visited:
|
||||
assert proc == 0, 'Multi process no supported'
|
||||
assert proc == 0, "Multi process no supported"
|
||||
db[pc] = db.setdefault(pc, 0) + 1
|
||||
edges.setdefault(lastpc, []).append(pc)
|
||||
lastpc = pc
|
||||
except Exception as e:
|
||||
print(f'#Failed to load saved state {filename} ({str(e)})')
|
||||
print(f"#Failed to load saved state {filename} ({str(e)})")
|
||||
|
||||
if args.pcfreq:
|
||||
print('#PC: frequency')
|
||||
print("#PC: frequency")
|
||||
for pc, freq in sorted(list(db.items()), key=lambda x: -x[1]):
|
||||
print(f'{pc:x}: {freq}')
|
||||
print(f"{pc:x}: {freq}")
|
||||
elif args.visited:
|
||||
print('#PC')
|
||||
print("#PC")
|
||||
for pc in list(db.keys()):
|
||||
print(f'{pc:x}')
|
||||
print(f"{pc:x}")
|
||||
elif args.bbs:
|
||||
assert len(set(edges['ROOT'])) == 1, "Something is wrong; there should be only one root"
|
||||
assert len(set(edges["ROOT"])) == 1, "Something is wrong; there should be only one root"
|
||||
bbs = set()
|
||||
for targets in list(edges.values()):
|
||||
if len(set(targets)) > 1:
|
||||
[bbs.add(x) for x in set(targets)]
|
||||
bbs.add('ROOT')
|
||||
print('#BBs')
|
||||
bbs.add("ROOT")
|
||||
print("#BBs")
|
||||
for origin in bbs:
|
||||
if origin not in edges:
|
||||
targets = set()
|
||||
@@ -72,7 +88,7 @@ elif args.bbs:
|
||||
if node in edges:
|
||||
targets = set(edges[node])
|
||||
else:
|
||||
targets = ['END']
|
||||
targets = ["END"]
|
||||
break
|
||||
|
||||
def p(x):
|
||||
|
||||
@@ -29,10 +29,20 @@ function install_cc_env {
|
||||
pip install awscli
|
||||
}
|
||||
|
||||
# install CodeClimate env in all conditions
|
||||
install_cc_env
|
||||
# Install black for initial formatting stage
|
||||
if [ "$1" == "format" ]; then
|
||||
pip install --update black
|
||||
fi
|
||||
|
||||
# Install CodeClimate env
|
||||
if [ "$1" != "format" ]; then
|
||||
install_cc_env
|
||||
fi
|
||||
|
||||
# Skip Manticore installation setup and teardown
|
||||
if [ "$1" != "env" ]; then
|
||||
install_solc
|
||||
install_mcore $1
|
||||
if [ "$1" != "format" ]; then
|
||||
install_solc
|
||||
install_mcore $1
|
||||
fi
|
||||
fi
|
||||
|
||||
+44
-34
@@ -9,29 +9,33 @@ import struct
|
||||
import qemu
|
||||
import gdb
|
||||
|
||||
logger = logging.getLogger('TRACE')
|
||||
logger = logging.getLogger("TRACE")
|
||||
|
||||
## We need to keep some complex objects in between hook invocations so we keep them
|
||||
## as globals. Tracing is inherently a single-threaded process, so using a
|
||||
## manticore context would be heavier than needed.
|
||||
stack_top = 0xc0000000
|
||||
stack_top = 0xC0000000
|
||||
stack_size = 0x20000
|
||||
initialized = False
|
||||
last_instruction = None
|
||||
in_helper = False
|
||||
|
||||
|
||||
def init_logging():
|
||||
class ContextFilter(logging.Filter):
|
||||
def filter(self, record):
|
||||
record.stateid = ''
|
||||
record.stateid = ""
|
||||
return True
|
||||
|
||||
logger.addFilter(ContextFilter())
|
||||
|
||||
|
||||
def dump_gdb(cpu, addr, count):
|
||||
for offset in range(addr, addr+count, 4):
|
||||
val = int(gdb.getM(offset) & 0xffffffff)
|
||||
for offset in range(addr, addr + count, 4):
|
||||
val = int(gdb.getM(offset) & 0xFFFFFFFF)
|
||||
val2 = int(cpu.read_int(offset))
|
||||
print(f'{offset:x}: g{val:08x} m{val2:08x}')
|
||||
print(f"{offset:x}: g{val:08x} m{val2:08x}")
|
||||
|
||||
|
||||
def cmp_regs(cpu, should_print=False):
|
||||
"""
|
||||
@@ -46,24 +50,26 @@ def cmp_regs(cpu, should_print=False):
|
||||
gdb_regs = gdb.getCanonicalRegisters()
|
||||
for name in sorted(gdb_regs):
|
||||
vg = gdb_regs[name]
|
||||
if name.endswith('psr'):
|
||||
name = 'apsr'
|
||||
if name.endswith("psr"):
|
||||
name = "apsr"
|
||||
v = cpu.read_register(name.upper())
|
||||
if should_print:
|
||||
logger.debug(f'{name} gdb:{vg:x} mcore:{v:x}')
|
||||
logger.debug(f"{name} gdb:{vg:x} mcore:{v:x}")
|
||||
if vg != v:
|
||||
if should_print:
|
||||
logger.warning('^^ unequal')
|
||||
logger.warning("^^ unequal")
|
||||
differing = True
|
||||
if differing:
|
||||
logger.debug(qemu.correspond(None))
|
||||
return differing
|
||||
|
||||
|
||||
def pre_mcore(state):
|
||||
# Start recording memory writes
|
||||
if state.cpu.instruction.mnemonic.lower() == 'svc':
|
||||
if state.cpu.instruction.mnemonic.lower() == "svc":
|
||||
state.cpu.memory.push_record_writes()
|
||||
|
||||
|
||||
def post_mcore(state, last_instruction):
|
||||
"""
|
||||
Handle syscalls (import memory) and bail if we diverge
|
||||
@@ -71,7 +77,7 @@ def post_mcore(state, last_instruction):
|
||||
global in_helper
|
||||
|
||||
# Synchronize qemu state to manticore's after a system call
|
||||
if last_instruction.mnemonic.lower() == 'svc':
|
||||
if last_instruction.mnemonic.lower() == "svc":
|
||||
# Synchronize all writes that have happened
|
||||
writes = state.cpu.memory.pop_record_writes()
|
||||
if writes:
|
||||
@@ -80,18 +86,17 @@ def post_mcore(state, last_instruction):
|
||||
gdb.setByte(addr, val[0])
|
||||
|
||||
# Write return val to gdb
|
||||
gdb_r0 = gdb.getR('R0')
|
||||
gdb_r0 = gdb.getR("R0")
|
||||
if gdb_r0 != state.cpu.R0:
|
||||
logger.debug(f"Writing 0x{state.cpu.R0:x} to R0 (overwriting 0x{gdb.getR('R0'):x})")
|
||||
for reg in state.cpu.canonical_registers:
|
||||
if reg.endswith('PSR') or reg in ('R15', 'PC'):
|
||||
if reg.endswith("PSR") or reg in ("R15", "PC"):
|
||||
continue
|
||||
val = state.cpu.read_register(reg)
|
||||
gdb.setR(reg, val)
|
||||
|
||||
|
||||
# Ignore Linux kernel helpers
|
||||
if (state.cpu.PC >> 16) == 0xffff:
|
||||
if (state.cpu.PC >> 16) == 0xFFFF:
|
||||
in_helper = True
|
||||
return
|
||||
|
||||
@@ -99,10 +104,10 @@ def post_mcore(state, last_instruction):
|
||||
# state to GDB as soon as we stop executing a helper.
|
||||
if in_helper:
|
||||
for reg in state.cpu.canonical_registers:
|
||||
if reg.endswith('PSR'):
|
||||
if reg.endswith("PSR"):
|
||||
continue
|
||||
# Don't sync pc
|
||||
if reg == 'R15':
|
||||
if reg == "R15":
|
||||
continue
|
||||
gdb.setR(reg, state.cpu.read_register(reg))
|
||||
in_helper = False
|
||||
@@ -111,43 +116,47 @@ def post_mcore(state, last_instruction):
|
||||
cmp_regs(state.cpu, should_print=True)
|
||||
state.abandon()
|
||||
|
||||
|
||||
def pre_qemu(state):
|
||||
# Nop for now, might need to do future sync state
|
||||
pass
|
||||
|
||||
|
||||
def post_qemu(state, last_mnemonic):
|
||||
if last_mnemonic.lower() == 'svc':
|
||||
if last_mnemonic.lower() == "svc":
|
||||
sync_svc(state)
|
||||
|
||||
|
||||
def sync_svc(state):
|
||||
"""
|
||||
Mirror some service calls in manticore. Happens after qemu executed a SVC
|
||||
instruction, but before manticore did.
|
||||
"""
|
||||
syscall = state.cpu.R7 # Grab idx from manticore since qemu could have exited
|
||||
syscall = state.cpu.R7 # Grab idx from manticore since qemu could have exited
|
||||
name = linux_syscalls.armv7[syscall]
|
||||
|
||||
logger.debug(f"Syncing syscall: {name}")
|
||||
|
||||
try:
|
||||
# Make sure mmap returns the same address
|
||||
if 'mmap' in name:
|
||||
returned = gdb.getR('R0')
|
||||
if "mmap" in name:
|
||||
returned = gdb.getR("R0")
|
||||
logger.debug(f"Syncing mmap ({returned:x})")
|
||||
state.cpu.write_register('R0', returned)
|
||||
if 'exit' in name:
|
||||
state.cpu.write_register("R0", returned)
|
||||
if "exit" in name:
|
||||
return
|
||||
except ValueError:
|
||||
for reg in state.cpu.canonical_registers:
|
||||
print(f'{reg}: {state.cpu.read_register(reg):x}')
|
||||
print(f"{reg}: {state.cpu.read_register(reg):x}")
|
||||
raise
|
||||
|
||||
|
||||
def initialize(state):
|
||||
"""
|
||||
Synchronize the stack and register state (manticore->qemu)
|
||||
"""
|
||||
logger.debug(f"Copying {stack_top - state.cpu.SP} bytes in the stack..")
|
||||
stack_bottom = min(state.cpu.SP, gdb.getR('SP'))
|
||||
stack_bottom = min(state.cpu.SP, gdb.getR("SP"))
|
||||
for address in range(stack_bottom, stack_top):
|
||||
b = state.cpu.read_int(address, 8)
|
||||
gdb.setByte(address, chr(b))
|
||||
@@ -161,20 +170,21 @@ def initialize(state):
|
||||
|
||||
# Sync gdb's regs
|
||||
for gdb_reg in gdb.getCanonicalRegisters():
|
||||
if gdb_reg.endswith('psr'):
|
||||
mcore_reg = 'APSR'
|
||||
if gdb_reg.endswith("psr"):
|
||||
mcore_reg = "APSR"
|
||||
else:
|
||||
mcore_reg = gdb_reg.upper()
|
||||
value = state.cpu.read_register(mcore_reg)
|
||||
gdb.setR(gdb_reg, value)
|
||||
|
||||
|
||||
def verify(argv):
|
||||
logger.debug(f"Verifying program \"{argv}\"")
|
||||
logger.debug(f'Verifying program "{argv}"')
|
||||
|
||||
# Address and stack_size are from linux.py
|
||||
# TODO(yan): Refactor these constants into a reachable value in platform
|
||||
qemu.start('arm', argv, va_size=stack_top, stack_size=stack_size)
|
||||
gdb.start('arm', argv)
|
||||
qemu.start("arm", argv, va_size=stack_top, stack_size=stack_size)
|
||||
gdb.start("arm", argv)
|
||||
|
||||
m = Manticore(argv[0], argv[1:])
|
||||
m.verbosity = 2
|
||||
@@ -210,11 +220,11 @@ def verify(argv):
|
||||
post_mcore(state, last_instruction)
|
||||
|
||||
# Kernel helpers are inline in QEMU; do nothing
|
||||
if (state.cpu.PC >> 16) == 0xffff:
|
||||
if (state.cpu.PC >> 16) == 0xFFFF:
|
||||
return
|
||||
|
||||
pre_qemu(state)
|
||||
last_mnemonic = [x.strip() for x in gdb.getInstruction().split(':')][1].split('\t')[0]
|
||||
last_mnemonic = [x.strip() for x in gdb.getInstruction().split(":")][1].split("\t")[0]
|
||||
gdb.stepi()
|
||||
post_qemu(state, last_mnemonic)
|
||||
|
||||
@@ -223,6 +233,7 @@ def verify(argv):
|
||||
|
||||
m.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = argv[1:]
|
||||
|
||||
@@ -231,4 +242,3 @@ if __name__ == "__main__":
|
||||
exit()
|
||||
|
||||
verify(args)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
on_rtd = os.environ.get('READTHEDOCS') == 'True'
|
||||
on_rtd = os.environ.get("READTHEDOCS") == "True"
|
||||
|
||||
|
||||
def rtd_dependent_deps():
|
||||
@@ -9,59 +9,45 @@ def rtd_dependent_deps():
|
||||
if on_rtd:
|
||||
return []
|
||||
else:
|
||||
return ['z3-solver']
|
||||
return ["z3-solver"]
|
||||
|
||||
|
||||
# If you update native_deps please update the `REQUIREMENTS_TO_IMPORTS` dict in `utils/install_helper.py`
|
||||
# (we need to know how to import a given native dependency so we can check if native dependencies are installed)
|
||||
native_deps = [
|
||||
'capstone==4.0.1',
|
||||
'pyelftools',
|
||||
'unicorn @ git+https://github.com/unicorn-engine/unicorn.git@778171fc9546c1fc3d1341ff1151eab379848ea0#subdirectory=bindings/python&egg=unicorn-1.0.1.post0',
|
||||
"capstone==4.0.1",
|
||||
"pyelftools",
|
||||
"unicorn @ git+https://github.com/unicorn-engine/unicorn.git@778171fc9546c1fc3d1341ff1151eab379848ea0#subdirectory=bindings/python&egg=unicorn-1.0.1.post0",
|
||||
]
|
||||
|
||||
extra_require = {
|
||||
'native': native_deps,
|
||||
'dev': native_deps + [
|
||||
'keystone-engine',
|
||||
'coverage',
|
||||
'nose',
|
||||
'Sphinx',
|
||||
],
|
||||
"native": native_deps,
|
||||
"dev": native_deps + ["keystone-engine", "coverage", "nose", "Sphinx"],
|
||||
# noks - no keystone
|
||||
'dev-noks': native_deps + [
|
||||
'coverage',
|
||||
'nose',
|
||||
'Sphinx',
|
||||
],
|
||||
'redis': [
|
||||
'redis',
|
||||
]
|
||||
"dev-noks": native_deps + ["coverage", "nose", "Sphinx"],
|
||||
"redis": ["redis"],
|
||||
}
|
||||
|
||||
|
||||
setup(
|
||||
name='manticore',
|
||||
description='Manticore is a symbolic execution tool for analysis of binaries and smart contracts.',
|
||||
url='https://github.com/trailofbits/manticore',
|
||||
author='Trail of Bits',
|
||||
version='0.2.5',
|
||||
packages=find_packages(exclude=['tests', 'tests.*']),
|
||||
python_requires='>=3.6',
|
||||
name="manticore",
|
||||
description="Manticore is a symbolic execution tool for analysis of binaries and smart contracts.",
|
||||
url="https://github.com/trailofbits/manticore",
|
||||
author="Trail of Bits",
|
||||
version="0.2.5",
|
||||
packages=find_packages(exclude=["tests", "tests.*"]),
|
||||
python_requires=">=3.6",
|
||||
install_requires=[
|
||||
'pyyaml',
|
||||
'wrapt',
|
||||
"pyyaml",
|
||||
"wrapt",
|
||||
# evm dependencies
|
||||
'pysha3',
|
||||
'prettytable',
|
||||
'pyevmasm==0.2.0',
|
||||
'rlp',
|
||||
'ply'
|
||||
] + rtd_dependent_deps(),
|
||||
"pysha3",
|
||||
"prettytable",
|
||||
"pyevmasm==0.2.0",
|
||||
"rlp",
|
||||
"ply",
|
||||
]
|
||||
+ rtd_dependent_deps(),
|
||||
extras_require=extra_require,
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'manticore = manticore.__main__:main'
|
||||
]
|
||||
}
|
||||
entry_points={"console_scripts": ["manticore = manticore.__main__:main"]},
|
||||
)
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
"""
|
||||
This file is requires so that unittest will be able to discover tests in children directories.
|
||||
"""
|
||||
"""
|
||||
|
||||
+1861
-232
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user