Fixes issue where the angr script printed out the wrong symbol bytes.

This commit is contained in:
Peter Goodman 2017-11-01 21:27:08 -04:00
parent 4f914e4eee
commit bc208dbd4d
4 changed files with 114 additions and 96 deletions

View File

@ -55,9 +55,8 @@ class DeepState(object):
def get_context(self):
raise NotImplementedError("Must be implemented by engine.")
@property
def context(self):
return self.get_context()
def create_symbol(self, name, size_in_bits):
raise NotImplementedError("Must be implemented by engine.")
def is_symbolic(self, val):
raise NotImplementedError("Must be implemented by engine.")
@ -89,6 +88,12 @@ class DeepState(object):
def add_constraint(self, expr):
raise NotImplementedError("Must be implemented by engine.")
@property
def context(self):
"""Gives convenient property-based access to a dictionary holding state-
local varaibles."""
return self.get_context()
def read_c_string(self, ea, concretize=True, constrain=False):
"""Read a NUL-terminated string from `ea`."""
assert isinstance(ea, (int, long))
@ -157,19 +162,32 @@ class DeepState(object):
return apis
def begin_test(self, info):
"""Begin processing the test associated with `info`."""
self.context['failed'] = False
self.context['abandoned'] = False
self.context['log'] = []
for level in LOG_LEVEL_TO_LOGGER:
self.context['stream_{}'.format(level)] = []
self.context['info'] = info
self.log_message(LOG_LEVEL_INFO, "Running {} from {}({})".format(
info.name, info.file_name, info.line_number))
apis = self.context['apis']
symbols = []
for i, ea in enumerate(xrange(apis['InputBegin'], apis['InputEnd'])):
symbol = self.create_symbol('DEEP_INPUT_{}'.format(i), 8)
self.write_uint8_t(ea, symbol)
symbols.append(symbol)
self.context['symbols'] = symbols
def log_message(self, level, message):
"""Log a message."""
"""Add `message` to the `level`-specific log as a `Stream` object for
deferred logging (at the end of the state)."""
assert level in LOG_LEVEL_TO_LOGGER
log = list(self.context['log'])
log = list(self.context['log']) # Make a shallow copy (needed for Angr).
if isinstance(message, (str, list, tuple)):
log.append((level, Stream([(str, "%s", None, message)])))
else:
@ -179,6 +197,7 @@ class DeepState(object):
self.context['log'] = log
def _concretize_bytes(self, byte_str):
"""Concretize the bytes of `byte_str`."""
new_bytes = []
for b in byte_str:
if isinstance(b, str):
@ -191,6 +210,8 @@ class DeepState(object):
return new_bytes
def _stream_to_message(self, stream):
"""Convert a `Stream` object into a single string message representing
the concatenation of all formatted stream entries."""
assert isinstance(stream, Stream)
message = []
for val_type, format_str, unpack_str, val_bytes in stream.entries:
@ -218,13 +239,14 @@ class DeepState(object):
return "".join(message)
def report(self):
"""Report on the pass/fail status of a test case, and dump its log."""
info = self.context['info']
apis = self.context['apis']
input_length, _ = self.read_uint32_t(apis['InputIndex'])
symbols = self.context['symbols']
input_bytes = []
for i in xrange(input_length):
ea = apis['InputBegin'] + i
b, _ = self.read_uint8_t(ea + i, concretize=True, constrain=True)
b = self.concretize(symbols[i], constrain=True)
input_bytes.append("{:02x}".format(b))
for level, stream in self.context['log']:
@ -234,12 +256,18 @@ class DeepState(object):
LOGGER.info("Input: {}".format(" ".join(input_bytes)))
def pass_test(self):
"""Notify the symbolic executor that this test has passed and stop
executing the current state."""
pass
def fail_test(self):
"""Notify the symbolic executor that this test has failed and stop
executing the current state."""
self.context['failed'] = True
def abandon_test(self):
"""Notify the symbolic executor that this test has been abandoned due to
some critical error and stop executing the current state."""
self.context['abandoned'] = True
def api_is_symbolic_uint(self, arg):
@ -344,6 +372,8 @@ class DeepState(object):
def _api_stream_int_float(self, level, format_ea, unpack_ea, uint64_ea,
val_type):
"""Read the format information and int or float value data from memory
and record it into a stream."""
level = self.concretize(level, constrain=True)
assert level in LOG_LEVEL_TO_LOGGER
@ -392,6 +422,8 @@ class DeepState(object):
self.context[stream_id] = stream
def api_log_stream(self, level):
"""Implements DeepState_LogStream, which converts the contents of a stream
for level `level` into a log for level `level`."""
level = self.concretize(level, constrain=True)
assert level in LOG_LEVEL_TO_LOGGER
stream_id = 'stream_{}'.format(level)

View File

@ -26,9 +26,9 @@ L = logging.getLogger("deepstate.angr")
L.setLevel(logging.INFO)
class AngrTest(DeepState):
class DeepAngr(DeepState):
def __init__(self, state=None, procedure=None):
super(AngrTest, self).__init__()
super(DeepAngr, self).__init__()
if procedure:
self.procedure = procedure
self.state = procedure.state
@ -48,27 +48,34 @@ class AngrTest(DeepState):
return False
return self.state.se.symbolic(val)
def create_symbol(self, name, size_in_bits):
return self.state.se.Unconstrained('name', size_in_bits)
def read_uintptr_t(self, ea, concretize=True, constrain=False):
next_ea = ea + (self.state.arch.bits // 8)
val = self.state.mem[ea].uintptr_t.resolved
addr_size_bytes = self.state.arch.bits // 8
endness = self.state.arch.memory_endness
next_ea = ea + addr_size_bytes
val = self.state.memory.load(ea, size=addr_size_bytes, endness=endness)
if concretize:
val = self.concretize(val, constrain=constrain)
return val, next_ea
def read_uint64_t(self, ea, concretize=True, constrain=False):
val = self.state.mem[ea].uint64_t.resolved
endness = self.state.arch.memory_endness
val = self.state.memory.load(ea, size=8, endness=endness)
if concretize:
val = self.concretize(val, constrain=constrain)
return val, ea + 8
def read_uint32_t(self, ea, concretize=True, constrain=False):
val = self.state.mem[ea].uint32_t.resolved
endness = self.state.arch.memory_endness
val = self.state.memory.load(ea, size=4, endness=endness)
if concretize:
val = self.concretize(val, constrain=constrain)
return val, ea + 4
def read_uint8_t(self, ea, concretize=True, constrain=False):
val = self.state.mem[ea].uint8_t.resolved
val = self.state.memory.load(ea, size=1)
if concretize:
val = self.concretize(val, constrain=constrain)
if isinstance(val, str):
@ -77,7 +84,7 @@ class AngrTest(DeepState):
return val, ea + 1
def write_uint8_t(self, ea, val):
self.state.mem[ea].uint8_t = val
self.state.memory.store(ea, val, size=1)
return ea + 1
def concretize(self, val, constrain=False):
@ -115,15 +122,15 @@ class AngrTest(DeepState):
return True
def pass_test(self):
super(AngrTest, self).pass_test()
super(DeepAngr, self).pass_test()
self.procedure.exit(0)
def fail_test(self):
super(AngrTest, self).fail_test()
super(DeepAngr, self).fail_test()
self.procedure.exit(1)
def abandon_test(self):
super(AngrTest, self).abandon_test()
super(DeepAngr, self).abandon_test()
self.procedure.exit(1)
@ -132,49 +139,41 @@ def hook_function(project, ea, cls):
project.hook(ea, cls(project=project))
def make_symbolic_input(state, input_begin_ea, input_end_ea):
"""Fill in the input data array with symbolic data."""
input_size = input_end_ea - input_begin_ea
data = state.se.Unconstrained('DEEPSTATE_INPUT', input_size * 8)
state.memory.store(input_begin_ea, data)
return data
class IsSymbolicUInt(angr.SimProcedure):
"""Implements DeepState_IsSymblicUInt, which returns 1 if its input argument
has more then one solutions, and zero otherwise."""
def run(self, arg):
return AngrTest(procedure=self).api_is_symbolic_uint(arg)
return DeepAngr(procedure=self).api_is_symbolic_uint(arg)
class Assume(angr.SimProcedure):
"""Implements _DeepState_Assume, which tries to inject a constraint."""
def run(self, arg):
AngrTest(procedure=self).api_assume(arg)
DeepAngr(procedure=self).api_assume(arg)
class Pass(angr.SimProcedure):
"""Implements DeepState_Pass, which notifies us of a passing test."""
def run(self):
AngrTest(procedure=self).api_pass()
DeepAngr(procedure=self).api_pass()
class Fail(angr.SimProcedure):
"""Implements DeepState_Fail, which notifies us of a failing test."""
def run(self):
AngrTest(procedure=self).api_fail()
DeepAngr(procedure=self).api_fail()
class Abandon(angr.SimProcedure):
"""Implements DeepState_Fail, which notifies us of a failing test."""
def run(self, reason):
AngrTest(procedure=self).api_abandon(reason)
DeepAngr(procedure=self).api_abandon(reason)
class SoftFail(angr.SimProcedure):
"""Implements DeepState_SoftFail, which notifies us of a failing test."""
def run(self):
AngrTest(procedure=self).api_soft_fail()
DeepAngr(procedure=self).api_soft_fail()
class ConcretizeData(angr.SimProcedure):
@ -182,28 +181,28 @@ class ConcretizeData(angr.SimProcedure):
programmer concretize some data in the exclusive range
`[begin_ea, end_ea)`."""
def run(self, begin_ea, end_ea):
return AngrTest(procedure=self).api_concretize_data(begin_ea, end_ea)
return DeepAngr(procedure=self).api_concretize_data(begin_ea, end_ea)
class ConcretizeCStr(angr.SimProcedure):
"""Implements the `Deeptate_ConcretizeCStr` API function, which lets the
programmer concretize a NUL-terminated string starting at `begin_ea`."""
def run(self, begin_ea):
return AngrTest(procedure=self).api_concretize_cstr(begin_ea)
return DeepAngr(procedure=self).api_concretize_cstr(begin_ea)
class StreamInt(angr.SimProcedure):
"""Implements _DeepState_StreamInt, which gives us an integer to stream, and
the format to use for streaming."""
def run(self, level, format_ea, unpack_ea, uint64_ea):
AngrTest(procedure=self).api_stream_int(level, format_ea, unpack_ea,
DeepAngr(procedure=self).api_stream_int(level, format_ea, unpack_ea,
uint64_ea)
class StreamFloat(angr.SimProcedure):
"""Implements _DeepState_StreamFloat, which gives us an double to stream, and
the format to use for streaming."""
def run(self, level, format_ea, unpack_ea, double_ea):
AngrTest(procedure=self).api_stream_float(level, format_ea, unpack_ea,
DeepAngr(procedure=self).api_stream_float(level, format_ea, unpack_ea,
double_ea)
@ -211,21 +210,21 @@ class StreamString(angr.SimProcedure):
"""Implements _DeepState_StreamString, which gives us an double to stream, and
the format to use for streaming."""
def run(self, level, format_ea, str_ea):
AngrTest(procedure=self).api_stream_string(level, format_ea, str_ea)
DeepAngr(procedure=self).api_stream_string(level, format_ea, str_ea)
class LogStream(angr.SimProcedure):
"""Implements DeepState_LogStream, which converts the contents of a stream for
level `level` into a log for level `level`."""
def run(self, level):
AngrTest(procedure=self).api_log_stream(level)
DeepAngr(procedure=self).api_log_stream(level)
class Log(angr.SimProcedure):
"""Implements DeepState_Log, which lets Angr intercept and handle the
printing of log messages from the simulated tests."""
def run(self, level, ea):
AngrTest(procedure=self).api_log(level, ea)
DeepAngr(procedure=self).api_log(level, ea)
def do_run_test(project, test, apis, run_state):
@ -235,12 +234,10 @@ def do_run_test(project, test, apis, run_state):
test.ea,
base_state=run_state)
mc = AngrTest(state=test_state)
mc = DeepAngr(state=test_state)
mc.begin_test(test)
del mc
make_symbolic_input(test_state, apis['InputBegin'], apis['InputEnd'])
errored = []
test_manager = angr.SimulationManager(
project=project,
@ -254,7 +251,7 @@ def do_run_test(project, test, apis, run_state):
sys.exc_info()[0], traceback.format_exc()))
for state in test_manager.deadended:
AngrTest(state=state).report()
DeepAngr(state=state).report()
for error in test_manager.errored:
print "Error", error.error
@ -314,7 +311,7 @@ def main():
# use it.
ea_of_api_table = project.kb.labels.lookup('DeepState_API')
mc = AngrTest(state=run_state)
mc = DeepAngr(state=run_state)
apis = mc.read_api_table(ea_of_api_table)
# Hook various functions.

View File

@ -31,9 +31,9 @@ L.setLevel(logging.INFO)
OUR_TERMINATION_REASON = "I DeepState'd it"
class MCoreTest(DeepState):
class DeepManticore(DeepState):
def __init__(self, state):
super(MCoreTest, self).__init__()
super(DeepManticore, self).__init__()
self.state = state
def __del__(self):
@ -45,6 +45,9 @@ class MCoreTest(DeepState):
def is_symbolic(self, val):
return manticore.utils.helpers.issymbolic(val)
def create_symbol(self, name, size_in_bits):
return self.state.new_symbolic_value(size_in_bits, name)
def read_uintptr_t(self, ea, concretize=True, constrain=False):
addr_size_bits = self.state.cpu.address_bit_size
next_ea = ea + (addr_size_bits // 8)
@ -115,103 +118,91 @@ class MCoreTest(DeepState):
return True
def pass_test(self):
super(MCoreTest, self).pass_test()
super(DeepManticore, self).pass_test()
raise TerminateState(OUR_TERMINATION_REASON, testcase=False)
def fail_test(self):
super(MCoreTest, self).fail_test()
super(DeepManticore, self).fail_test()
raise TerminateState(OUR_TERMINATION_REASON, testcase=False)
def abandon_test(self):
super(MCoreTest, self).abandon_test()
super(DeepManticore, self).abandon_test()
raise TerminateState(OUR_TERMINATION_REASON, testcase=False)
def make_symbolic_input(state, input_begin_ea, input_end_ea):
"""Fill in the input data array with symbolic data."""
input_size = input_end_ea - input_begin_ea
data = []
for i in xrange(input_end_ea - input_begin_ea):
input_byte = state.new_symbolic_value(8, "DEEPSTATE_INPUT_{}".format(i))
data.append(input_byte)
state.cpu.write_int(input_begin_ea + i, input_byte, 8)
return data
def hook_IsSymbolicUInt(state, arg):
"""Implements DeepState_IsSymblicUInt, which returns 1 if its input argument
has more then one solutions, and zero otherwise."""
return MCoreTest(state).api_is_symbolic_uint(arg)
return DeepManticore(state).api_is_symbolic_uint(arg)
def hook_Assume(state, arg):
"""Implements _DeepState_Assume, which tries to inject a constraint."""
MCoreTest(state).api_assume(arg)
DeepManticore(state).api_assume(arg)
def hook_StreamInt(state, level, format_ea, unpack_ea, uint64_ea):
"""Implements _DeepState_StreamInt, which gives us an integer to stream, and
the format to use for streaming."""
MCoreTest(state).api_stream_int(level, format_ea, unpack_ea, uint64_ea)
DeepManticore(state).api_stream_int(level, format_ea, unpack_ea, uint64_ea)
def hook_StreamFloat(state, level, format_ea, unpack_ea, double_ea):
"""Implements _DeepState_StreamFloat, which gives us an double to stream, and
the format to use for streaming."""
MCoreTest(state).api_stream_float(level, format_ea, unpack_ea, double_ea)
DeepManticore(state).api_stream_float(level, format_ea, unpack_ea, double_ea)
def hook_StreamString(state, level, format_ea, str_ea):
"""Implements _DeepState_StreamString, which gives us an double to stream, and
the format to use for streaming."""
MCoreTest(state).api_stream_string(level, format_ea, str_ea)
DeepManticore(state).api_stream_string(level, format_ea, str_ea)
def hook_LogStream(state, level):
"""Implements DeepState_LogStream, which converts the contents of a stream for
level `level` into a log for level `level`."""
MCoreTest(state).api_log_stream(level)
DeepManticore(state).api_log_stream(level)
def hook_Pass(state):
"""Implements DeepState_Pass, which notifies us of a passing test."""
MCoreTest(state).api_pass()
DeepManticore(state).api_pass()
def hook_Fail(state):
"""Implements DeepState_Fail, which notifies us of a passing test."""
MCoreTest(state).api_fail()
DeepManticore(state).api_fail()
def hook_Abandon(state, reason):
"""Implements DeepState_Abandon, which notifies us that a problem happened
in DeepState."""
MCoreTest(state).api_abandon(reason)
DeepManticore(state).api_abandon(reason)
def hook_SoftFail(state):
"""Implements DeepState_Fail, which notifies us of a passing test."""
MCoreTest(state).api_soft_fail()
DeepManticore(state).api_soft_fail()
def hook_ConcretizeData(state, begin_ea, end_ea):
"""Implements the `Deeptate_ConcretizeData` API function, which lets the
programmer concretize some data in the exclusive range
`[begin_ea, end_ea)`."""
return MCoreTest(state).api_concretize_data(begin_ea, end_ea)
return DeepManticore(state).api_concretize_data(begin_ea, end_ea)
def hook_ConcretizeCStr(state, begin_ea):
"""Implements the `Deeptate_ConcretizeCStr` API function, which lets the
programmer concretize a NUL-terminated string starting at `begin_ea`."""
return MCoreTest(state).api_concretize_cstr(begin_ea)
return DeepManticore(state).api_concretize_cstr(begin_ea)
def hook_Log(state, level, ea):
"""Implements DeepState_Log, which lets Manticore intercept and handle the
printing of log messages from the simulated tests."""
MCoreTest(state).api_log(level, ea)
DeepManticore(state).api_log(level, ea)
def hook(func):
@ -224,7 +215,7 @@ def done_test(_, state, state_id, reason):
L.error("State {} terminated for unknown reason: {}".format(
state_id, reason))
return
mc = MCoreTest(state)
mc = DeepManticore(state)
mc.report()
@ -235,12 +226,10 @@ def do_run_test(state, apis, test):
m.verbosity(1)
state = m.initial_state
mc = MCoreTest(state)
mc = DeepManticore(state)
mc.begin_test(test)
del mc
make_symbolic_input(state, apis['InputBegin'], apis['InputEnd'])
m.add_hook(apis['IsSymbolicUInt'], hook(hook_IsSymbolicUInt))
m.add_hook(apis['ConcretizeData'], hook(hook_ConcretizeData))
m.add_hook(apis['ConcretizeCStr'], hook(hook_ConcretizeCStr))
@ -271,7 +260,7 @@ def run_tests(args, state, apis):
"""Run all of the test cases."""
pool = multiprocessing.Pool(processes=max(1, args.num_workers))
results = []
mc = MCoreTest(state)
mc = DeepManticore(state)
tests = mc.find_test_cases()
L.info("Running {} tests across {} workers".format(
@ -310,7 +299,7 @@ def main():
setup_ea = m._get_symbol_address('DeepState_Setup')
setup_state = m._initial_state
mc = MCoreTest(setup_state)
mc = DeepManticore(setup_state)
ea_of_api_table = m._get_symbol_address('DeepState_API')
apis = mc.read_api_table(ea_of_api_table)

View File

@ -16,22 +16,22 @@
#include <deepstate/DeepState.hpp>
//TEST(Streaming, BasicLevels) {
// LOG(DEBUG) << "This is a debug message";
// LOG(INFO) << "This is an info message";
// LOG(WARNING) << "This is a warning message";
// LOG(ERROR) << "This is a error message";
// LOG(INFO) << "This is a info message again";
// ASSERT(true) << "This should not be printed.";
//}
//
//TEST(Streaming, BasicTypes) {
// LOG(INFO) << 'a';
// LOG(INFO) << 1;
// LOG(INFO) << 1.0;
// LOG(INFO) << "string";
// LOG(INFO) << nullptr;
//}
TEST(Streaming, BasicLevels) {
LOG(DEBUG) << "This is a debug message";
LOG(INFO) << "This is an info message";
LOG(WARNING) << "This is a warning message";
LOG(ERROR) << "This is a error message";
LOG(INFO) << "This is a info message again";
ASSERT(true) << "This should not be printed.";
}
TEST(Streaming, BasicTypes) {
LOG(INFO) << 'a';
LOG(INFO) << 1;
LOG(INFO) << 1.0;
LOG(INFO) << "string";
LOG(INFO) << nullptr;
}
TEST(Formatting, OverridePrintf) {
printf("hello string=%s hex_lower=%x hex_upper=%X octal=%o char=%c dec=%d"