From bc208dbd4da0e8cdd96d25ef4d7c637e45780b6e Mon Sep 17 00:00:00 2001 From: Peter Goodman Date: Wed, 1 Nov 2017 21:27:08 -0400 Subject: [PATCH] Fixes issue where the angr script printed out the wrong symbol bytes. --- bin/deepstate/common.py | 46 ++++++++++++++++--- bin/deepstate/main_angr.py | 71 ++++++++++++++--------------- bin/deepstate/main_manticore.py | 61 ++++++++++--------------- examples/StreamingAndFormatting.cpp | 32 ++++++------- 4 files changed, 114 insertions(+), 96 deletions(-) diff --git a/bin/deepstate/common.py b/bin/deepstate/common.py index edfc3f6..3d90153 100644 --- a/bin/deepstate/common.py +++ b/bin/deepstate/common.py @@ -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) diff --git a/bin/deepstate/main_angr.py b/bin/deepstate/main_angr.py index 212b3fa..96ee536 100644 --- a/bin/deepstate/main_angr.py +++ b/bin/deepstate/main_angr.py @@ -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. diff --git a/bin/deepstate/main_manticore.py b/bin/deepstate/main_manticore.py index 4043b34..92e4689 100644 --- a/bin/deepstate/main_manticore.py +++ b/bin/deepstate/main_manticore.py @@ -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) diff --git a/examples/StreamingAndFormatting.cpp b/examples/StreamingAndFormatting.cpp index f12cbc6..960aa70 100644 --- a/examples/StreamingAndFormatting.cpp +++ b/examples/StreamingAndFormatting.cpp @@ -16,22 +16,22 @@ #include -//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"