diff --git a/haystack/reverse/cli.py b/haystack/reverse/cli.py index 30a7fc3..48b3b8a 100644 --- a/haystack/reverse/cli.py +++ b/haystack/reverse/cli.py @@ -21,7 +21,7 @@ def show_hex(args): """ Show the Hex values for the record at that address. """ - memory_handler = cli.get_memory_handler(args) + memory_handler = cli.make_memory_handler(args) process_context = memory_handler.get_reverse_context() ctx = process_context.get_context_for_address(args.address) try: @@ -35,10 +35,10 @@ def show_hex(args): def show_predecessors_cmdline(args): """ Show the predecessors that point to a record at a particular address. - :param opt: + :param args: cmdline args :return: """ - memory_handler = cli.get_memory_handler(args) + memory_handler = cli.make_memory_handler(args) process_context = memory_handler.get_reverse_context() ctx = process_context.get_context_for_address(args.address) try: @@ -58,7 +58,7 @@ def show_predecessors_cmdline(args): def reverse_show_cmdline(args): """ Show the record at a specific address. """ - memory_handler = cli.get_memory_handler(args) + memory_handler = cli.make_memory_handler(args) process_context = memory_handler.get_reverse_context() ctx = process_context.get_context_for_address(args.address) try: @@ -73,7 +73,7 @@ def reverse_cmdline(args): """ Reverse """ from haystack.reverse import api as rapi # get the memory handler adequate for the type requested - memory_handler = cli.get_memory_handler(args) + memory_handler = cli.make_memory_handler(args) # do the search rapi.reverse_instances(memory_handler) return @@ -111,7 +111,7 @@ def reverse_parents(): desc = REVERSE_PARENT_DESC rootparser = cli.base_argparser(program_name=os.path.basename(sys.argv[0]), description=desc) rootparser.add_argument('address', type=argparse_utils.int16, action='store', default=None, - help='Hex address of the child structure') + help='Hex address of the child structure') rootparser.set_defaults(func=show_predecessors_cmdline) opts = rootparser.parse_args(argv) # apply verbosity diff --git a/haystack/reverse/heuristics/constraints.py b/haystack/reverse/heuristics/constraints.py index 3dcaa1c..3e1c1c8 100644 --- a/haystack/reverse/heuristics/constraints.py +++ b/haystack/reverse/heuristics/constraints.py @@ -15,6 +15,8 @@ class ConstraintsReverser(object): + # TODO: ConstraintsReverser need to work on RecordInstance and not RecordType + # so that value of fields can be evaluated. def __init__(self, memory_handler): self.__memory_handler = memory_handler self.__process_context = memory_handler.get_reverse_context() @@ -50,6 +52,7 @@ def verify(self, _record_type, members): continue values = [] for _item in records: + # BUG AttributeError: 'AnonymousRecord' object has no attribute 'get_value_for_field' val = _item.get_value_for_field(field) if field.is_pointer(): values.append(hex(val)) diff --git a/haystack/reverse/heuristics/pointertypes.py b/haystack/reverse/heuristics/pointertypes.py index dbdef38..4720b80 100644 --- a/haystack/reverse/heuristics/pointertypes.py +++ b/haystack/reverse/heuristics/pointertypes.py @@ -40,18 +40,18 @@ def reverse_record(self, _context, _record): """ # If you want to cache resolved infos, it still should be decided by # the caller - pointer_fields = [field for field in _record.record_type.get_fields() if field.is_pointer()] + pointer_fields = [field for field in _record.get_fields() if field.type.is_pointer()] log.debug('got %d pointer fields', len(pointer_fields)) for field in pointer_fields: - #value = field.value + value = field.value # get the FieldInstance, and its value. - # FIXME This is messed up, this set_pointee_addr method should be in FieldInstance - value = _record.get_field(field.name).value - field.set_pointee_addr(value) # default + # ? FIX ME This is messed up, this set_pointee_addr method should be in FieldInstance + #value = _record.get_field(field.name).value + #field.set_pointee_addr(value) # default # FIXME field.set_resolved() # What ? # + if value is unaligned, mark it as cheesy if value % self._target.get_word_size(): - field.comment = 'Unaligned pointer value' + field.type.comment = 'Unaligned pointer value' # + ask _memory_handler for the context for that value try: ctx = context.get_context_for_address(self._memory_handler, value) # no error expected. @@ -60,10 +60,10 @@ def reverse_record(self, _context, _record): # value is a pointer, but not to a heap. m = self._memory_handler.get_mapping_for_address(value) # field.set_child_desc('ext_lib @%0.8x %s' % (m.start, m.pathname)) - field.set_pointer_to_ext_lib() - field.set_pointee_ctype('void') + field.type.set_pointer_to_ext_lib() + field.type.set_pointee_ctype('void') # TODO: Function pointer ? - field.name = 'ptr_ext_lib_%d' % field.offset + field.name = 'ptr_ext_lib_%d' % field.type.offset # if value in self.__functions_pointers: # size, bbs, name = self.__functions_pointers[value] # field.name = 'func_ptr_%s_%d' % (name, field.offset) @@ -75,35 +75,36 @@ def reverse_record(self, _context, _record): # there is no child structure member at pointed value. except (IndexError, ValueError) as e: log.debug('there is no child structure enclosing pointed value %0.8x - %s', value, e) - field.set_pointee_desc('MemoryHandler management space') - field.set_pointee_ctype('void') - field.name = 'ptr_void_%d' % field.offset + field.type.set_pointee_desc('MemoryHandler management space') + field.type.set_pointee_ctype('void') + field.name = 'ptr_void_%d' % field.type.offset continue # structure found ## log.debug('Looking at child id:0x%x str:%s', tgt.address, tgt.to_string()) # we always point on structure, not field - field.set_pointee_addr(tgt.address) + # FIXME this is really a meta pointer instance thing + field.type.set_pointee_addr(tgt.address) offset = value - tgt.address try: tgt_field = tgt.get_field_at_offset(offset) # @throws IndexError except IndexError as e: # there is no field right there log.debug('there is no field at pointed value %0.8x. May need splitting byte field - %s', value, e) - field.set_pointee_desc('Badly reversed field') - field.set_pointee_ctype('void') - field.name = 'ptr_void_%d' % field.offset + field.type.set_pointee_desc('Badly reversed field') + field.type.set_pointee_ctype('void') + field.name = 'ptr_void_%d' % field.type.offset continue # do not put exception for field 0. structure name should appears # anyway. - field.set_pointee_desc('%s.%s' % (tgt.name, tgt_field.name)) + field.type.set_pointee_desc('%s.%s' % (tgt.name, tgt_field.name)) # TODO: # do not complexify code by handling target field type, # lets start with simple structure type pointer, # later we would need to use tgt_field.ctypes depending on field # offset - field.set_pointee_ctype(tgt.name) + field.type.set_pointee_ctype(tgt.name) # field.name = '%s_%s_%d' % (tgt.name, tgt_field.name, field.offset) - field.name = 'ptr_%s_%d' % (tgt.name, field.offset) + field.name = 'ptr_%s_%d' % (tgt.name, field.type.offset) # all _record.set_reverse_level(self._reverse_level) diff --git a/haystack/reverse/heuristics/reversers.py b/haystack/reverse/heuristics/reversers.py index 064fe7b..d4395c7 100644 --- a/haystack/reverse/heuristics/reversers.py +++ b/haystack/reverse/heuristics/reversers.py @@ -474,9 +474,9 @@ def reverse_record(self, heap_context, _record): # for child in struct.getPointerFields()) #target_struct_addr # target_struct_addr - pointer_fields = [f for f in _record.get_fields() if f.is_pointer()] + pointer_fields = [f for f in _record.get_fields() if f.type.is_pointer()] for f in pointer_fields: - pointee_addr = f._child_addr + pointee_addr = f.value # f._child_addr # we always feed these two # TODO: if a Node is out of heap/segment, replace it by a virtual node & color representing # the foreign heap/segment @@ -675,8 +675,8 @@ def reverse_context(self, _context): def reverse_record(self, _context, _record): for field in _record.get_fields(): - addr = _record.address + field.offset - if field.is_string(): - maxlen = len(field) - value = _record.get_value_for_field(field, maxlen+10) + addr = _record.address + field.type.offset + if field.type.is_string(): + maxlen = len(field.type) + value = field.get_value_for_field(maxlen+10) self.fout.write("0x%x,%d,%s\n" % (addr, maxlen, value)) diff --git a/haystack/reverse/structure.py b/haystack/reverse/structure.py index 61d6eb3..abeab10 100644 --- a/haystack/reverse/structure.py +++ b/haystack/reverse/structure.py @@ -552,7 +552,7 @@ class %s(ctypes.Structure): # %s # FIXME maybe instances field and record should have no name. -# __str__ should combinae type.name with @address +# __str__ should combine type.name with @address class FieldInstance(object): """ The instance of a Field @@ -571,6 +571,7 @@ def type(self): return self._field_decl def get_value_for_field(self, max_len=120): + # call the .value property instead my_bytes = self.__get_value_for_field_inner(max_len) if isinstance(my_bytes, str): bl = len(str(my_bytes)) diff --git a/scripts/haystack-gui b/scripts/haystack-gui deleted file mode 100644 index 75b55e6..0000000 --- a/scripts/haystack-gui +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2011 Loic Jaquemet loic.jaquemet+python@gmail.com -# - -__author__ = "Loic Jaquemet loic.jaquemet+python@gmail.com" - -__doc__ = ''' - Find a C Struct in process memory. - -''' - -import os, sys - -if __name__ == "__main__": - #debug - sys.path.insert(0,os.getcwd()) - sys.path.insert(0,os.path.join(os.getcwd(),'haystack')) - from haystack import gui - from haystack.gui import gui - gui.main(sys.argv[1:]) - - diff --git a/test/haystack/reverse/test_cli.py b/test/haystack/reverse/test_cli.py new file mode 100644 index 0000000..cba30a2 --- /dev/null +++ b/test/haystack/reverse/test_cli.py @@ -0,0 +1,59 @@ +from __future__ import print_function + +import logging +import unittest +import sys +try: + from unittest import mock +except ImportError: + import mock + +from haystack.reverse import cli +from haystack.reverse import config + +log = logging.getLogger("test_reverse_cli") + + +class TestReverseBasic(unittest.TestCase): + + def setUp(self): + self.dumpname = 'dmp://./test/dumps/minidump/cmd.dmp' + config.remove_cache_folder(self.dumpname) + + def tearDown(self): + config.remove_cache_folder(self.dumpname) + + def test_reverse(self): + testargs = ["haystack-reverse", self.dumpname] + with mock.patch.object(sys, 'argv', testargs): + # no exception + cli.reverse() + # test cache/headers_values.py + # test cache/graph.gexf + # test cache/graph.heaps.gexf + # test cache/*.strings + + def test_reverse_hex(self): + testargs = ["haystack-reverse-hex", self.dumpname, '0x543f60'] + # TODO, make a hexdump like output ? + with mock.patch.object(sys, 'argv', testargs): + cli.reverse_hex() + # output is + # b'USERPROFILE=C:\\Documents and Settings\\UserName\x00\x00' + + def test_reverse_parent(self): + testargs = ["haystack-reverse-parents", self.dumpname, '0x543f60'] + with mock.patch.object(sys, 'argv', testargs): + cli.reverse_parents() + # not exception + + def test_reverse_show(self): + testargs = ["haystack-reverse-show", self.dumpname, '0x543f60'] + with mock.patch.object(sys, 'argv', testargs): + cli.reverse_show() + # not exception + + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + unittest.main(verbosity=2)