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:
Eric Hennenfent
2019-05-23 15:19:24 -05:00
committed by GitHub
parent 581f43d144
commit 451965f03a
197 changed files with 149278 additions and 159606 deletions
+4
View File
@@ -48,6 +48,10 @@ plugins:
enabled: false
E701:
enabled: false
E203:
enabled: false
W503:
enabled: false
sonar-python:
enabled: false
config:
+5
View File
@@ -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:
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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"))
+58 -46
View File
@@ -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)}"
)
+6 -8
View File
@@ -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
View File
@@ -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")
+11 -7
View File
@@ -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", "")
+11 -12
View File
@@ -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
View File
@@ -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}")
+9 -7
View File
@@ -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}")
+32 -21
View File
@@ -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}")
+23 -16
View File
@@ -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}")
+6 -8
View File
@@ -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
View File
@@ -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}")
+8 -5
View File
@@ -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"])
+6 -3
View File
@@ -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
+2 -7
View File
@@ -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 = ""
+11 -11
View File
@@ -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)
+20 -16
View File
@@ -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
View File
@@ -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()
+5 -5
View File
@@ -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()
+12 -11
View File
@@ -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.")
+8 -8
View File
@@ -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 -4
View File
@@ -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()
+1 -2
View File
@@ -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()
+4 -4
View File
@@ -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
+2 -1
View File
@@ -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
View File
@@ -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()
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+1
View File
@@ -4,4 +4,5 @@ from .solver import * # noqa
from . import operators as Operators # noqa
import logging
logger = logging.getLogger(__name__)
+85 -37
View File
@@ -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)
+75 -42
View File
@@ -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):
+15 -5
View File
@@ -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))
+91 -71
View File
@@ -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")
+121 -92
View File
@@ -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
View File
@@ -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 = []
+20 -7
View File
@@ -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
View File
@@ -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")
+15 -3
View File
@@ -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
View File
@@ -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:
+54 -54
View File
@@ -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))
+30 -16
View File
@@ -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
View File
@@ -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
View File
@@ -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:
File diff suppressed because it is too large Load Diff
+92 -34
View File
@@ -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),
]
+29 -22
View File
@@ -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)
+91 -52
View File
@@ -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})"
+6 -1
View File
@@ -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
View File
@@ -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())
File diff suppressed because it is too large Load Diff
+149 -105
View File
@@ -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
View File
@@ -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))
+6 -3
View File
@@ -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):
+19 -17
View File
@@ -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)
+1
View File
@@ -6,6 +6,7 @@ import capstone as cs
class Instruction:
"""Capstone-like instruction to be used internally
"""
@property
@abstractmethod
def address(self):
+1 -1
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+89 -55
View File
@@ -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
+12 -6
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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):
+5 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -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):
+9 -7
View File
@@ -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
+8 -10
View File
@@ -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
View File
@@ -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:
+7 -4
View File
@@ -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
View File
@@ -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)
+9 -8
View File
@@ -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)
+77 -39
View File
@@ -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
+7 -3
View File
@@ -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)
+4 -8
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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:
+48 -39
View File
@@ -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)
+10 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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])
+9
View File
@@ -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
View File
@@ -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."""
+80 -47
View File
@@ -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
View File
@@ -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):
+14 -4
View File
@@ -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
View File
@@ -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)
+26 -40
View File
@@ -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
View File
@@ -1,3 +1,3 @@
"""
This file is requires so that unittest will be able to discover tests in children directories.
"""
"""
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