def _load_heap_constraints(self): """ Init the constraints on the heap module :return: """ parser = constraints.ConstraintsConfigHandler() return parser.read(self._heap_constraint_filename)
def search_cmdline(args): """ Search for instance of a record_type in the allocated memory of a process. """ # get the memory handler adequate for the type requested memory_handler = get_memory_handler(args) # try to load constraints my_constraints = None if args.constraints_file: handler = constraints.ConstraintsConfigHandler() my_constraints = handler.read(args.constraints_file.name) # get the python record type modulename, sep, classname = args.record_type_name.rpartition('.') module = None try: module = memory_handler.get_model().import_module(modulename) except ImportError as e: log.error('sys.path is %s', sys.path) raise e record_type = getattr(module, classname) # do the search results = api.search_record(memory_handler, record_type, my_constraints, extended_search=args.extended) # output handling try: ret = get_output(memory_handler, results, args.output) # print output on stdout print(ret) except Exception as e: log.error(e) finally: if args.interactive: print('results are local variable "results"') import code code.interact(local=locals()) return
def search_heap_direct(self, memdumpname, start_address_mapping): # we need a memory dump loader self.memory_handler = dump_loader.load(memdumpname) my_model = self.memory_handler.get_model() module_name = self._init_module_name(self.memory_handler) # import the module with the right arch heap_module = my_model.import_module(module_name) log.debug('the heap module loaded is %s', module_name) # load the constraints constraint_filename = self._init_constraints_filename(heap_module) parser = constraints.ConstraintsConfigHandler() my_constraints = parser.read(constraint_filename) m = self.memory_handler.get_mapping_for_address(start_address_mapping) my_searcher = searcher.AnyOffsetRecordSearcher( self.memory_handler, my_constraints, [m], update_cb=partial(self.print_cb, self.memory_handler)) heap_record_name = self._init_heap_record_name() heap_struct = getattr(heap_module, heap_record_name) results = my_searcher._load_at(m, start_address_mapping, heap_struct, depth=5) #print haystack.output_to_python(memory_handler, [results])[0][0].toString() return results
def _make_dual_arch_ctypes(self): # dual arch module_name_32 = 'haystack.allocators.win32.win7_32' _win7_32 = target.TargetPlatform.make_target_win_32('win7') _model_32 = model.Model(_win7_32.get_target_ctypes()) _win7_32_module = _model_32.import_module(module_name_32) # TODO make dual optional module_name_64 = 'haystack.allocators.win32.win7_64' _win7_64 = target.TargetPlatform.make_target_win_64('win7') _model_64 = model.Model(_win7_64.get_target_ctypes()) _win7_64_module = _model_64.import_module(module_name_64) # different arch have different recors types. parser = constraints.ConstraintsConfigHandler() constraint_filename = os.path.join(os.path.dirname(sys.modules[__name__].__file__), 'win7heap32.constraints') _constraints_32 = parser.read(constraint_filename) constraint_filename = os.path.join(os.path.dirname(sys.modules[__name__].__file__), 'win7heap64.constraints') _constraints_64 = parser.read(constraint_filename) # KERNEL AS kas32 = (0x8000000, 0xFFFFFFFF) kas64 = (0xFFFF080000000000, 0xFFFFFFFFFFFFFFFF) _cpu = dict() _cpu[32] = {'model': _model_32, 'target': _win7_32, 'module': _win7_32_module, 'constraints': _constraints_32, 'signature_offset': 100, 'kernel_as': kas32} _cpu[64] = {'model': _model_64, 'target': _win7_64, 'module': _win7_64_module, 'constraints': _constraints_64, 'signature_offset': 160, 'kernel_as': kas64} return _cpu
def test_search_with_constraints(self): # now add some constraints to the search for struct_test3 handler = constraints.ConstraintsConfigHandler() my_constraints = handler.read('test/src/ctypes3.constraints') results = haystack.search_record(self.memory_handler, self.ctypes3.struct_test3, my_constraints) # all valid record addresses are in self.offsets valid = self.offsets['test3'] self.assertEqual(len(results), len(valid)) for record, addr in results: self.assertIn(addr, valid) # search struct_Node with constraints results = haystack.search_record(self.memory_handler, self.ctypes3.struct_Node, my_constraints) # check the string output out = haystack.output_to_string(self.memory_handler, results) valid = self.offsets['test1'] self.assertEqual(len(results), len(valid)) for x in valid: self.assertIn(hex(x), out) # all valid record addresses are in self.offsets for record, addr in results: self.assertIn(addr, valid)
def setUp(self): self.memory_handler = dump_loader.load('test/src/test-ctypes6.32.dump') self.memdumpname = 'test/src/test-ctypes6.32.dump' self._load_offsets_values(self.memdumpname) sys.path.append('test/src/') my_model = self.memory_handler.get_model() self.ctypes6_gen32 = my_model.import_module("ctypes6_gen32") handler = constraints.ConstraintsConfigHandler() my_constraints = handler.read('test/src/ctypes6.constraints') self.x32_validator = ctypes6.CTypes6Validator(self.memory_handler, my_constraints, self.ctypes6_gen32) self.offset = self.offsets['test1'][0] self.m = self.memory_handler.get_mapping_for_address(self.offset) self.usual = self.m.read_struct(self.offset, self.ctypes6_gen32.struct_usual) # complex self.o_rootA = self.offsets['rootA'][0] self.rootA = self.m.read_struct(self.o_rootA, self.ctypes6_gen32.struct_Node) self.o_rootB = self.offsets['rootB'][0] self.rootB = self.m.read_struct(self.o_rootB, self.ctypes6_gen32.struct_Node) self.o_rootC = self.offsets['rootC'][0] self.rootC = self.m.read_struct(self.o_rootC, self.ctypes6_gen32.struct_Node)
def test_search(self): handler = constraints.ConstraintsConfigHandler() my_constraints = handler.read('test/src/ctypes6.constraints') results = api.search_record(self.memory_handler, self.node, my_constraints) # 2 from test1 # 3 from test_pointer_to_list # the rest have bad values in constrainged fields self.assertEqual(len(results), 2 + 3) # FIXME: that is a weird idea, that allocations are ordered that way (node1, offset1), (node2, offset2) = results[:2] self.assertEqual(node1.val1, 0xdeadbeef) self.assertEqual(node1.val2, 0xffffffff) self.assertEqual(node2.val1, 0xdeadbabe) self.assertEqual(node2.val2, 0xffffffff) # FIXME there is a circular reference in json. #with self.assertRaises(ValueError): # api.output_to_json(self.memory_handler, results) #self.assertEqual(node2s['val1'], 0xdeadbabe) #self.assertEqual(node2s['val2'], 0xffffffff) model = self.memory_handler.get_model() #import code #code.interact(local=locals()) x = api.output_to_pickle(self.memory_handler, results) rest = pickle.loads(x) return
def _init_haystack(self): self.my_name = self.config.PROFILE # get the structure name and type self.modulename, sep, self.classname = self.config.RECORD_NAME.rpartition( '.') # parse the constraint file if self.config.CONSTRAINT_FILE: handler = constraints.ConstraintsConfigHandler() self.my_constraints = handler.read(self.config.CONSTRAINT_FILE) else: self.my_constraints = None return
def refresh(args): """ Default function for the refresh command line option. Try to map a Structure from a specific offset in memory. Returns it in pickled or text format. See the command line --help . """ # we need an int memory_address = args.addr # get the memory handler adequate for the type requested memory_handler = _get_memory_handler(args) # print output on stdout rtype = _get_output_style(args) # check the validity of the address heap = memory_handler.is_valid_address_value(memory_address) if not heap: log.error("the address is not accessible in the memoryMap") raise ValueError("the address is not accessible in the memoryMap") # get the structure name modulename, sep, classname = args.struct_name.rpartition('.') module = memory_handler.get_model().import_module(modulename) struct_type = getattr(module, classname) # load the record result = api.load_record(memory_handler, struct_type, memory_address) results = [result] if args.validate: my_constraints = None if args.constraints_file: handler = constraints.ConstraintsConfigHandler() my_constraints = handler.read(args.constraints_file.name) validation = api.validate_record(memory_handler, result[0], my_constraints) if args.interactive: import code code.interact(local=locals()) # output handling ret = None if rtype == 'string': ret = api.output_to_string(memory_handler, results) elif rtype == 'python': ret = api.output_to_python(memory_handler, results) elif rtype == 'json': ret = api.output_to_json(memory_handler, results) elif rtype == 'pickled': ret = api.output_to_pickle(memory_handler, results) else: raise ValueError('unknown output format') print ret if args.validate: print 'Validated', validation return
def show_cmdline(args): """Cast the bytes at this address into a record_type. """ # we need an int memory_address = args.address # get the memory handler adequate for the type requested memory_handler = get_memory_handler(args) # check the validity of the address heap = memory_handler.is_valid_address_value(memory_address) if not heap: log.error("the address is not accessible in the memoryMap") raise ValueError("the address is not accessible in the memoryMap") # get the structure name modulename, sep, classname = args.record_type_name.rpartition('.') module = None try: module = memory_handler.get_model().import_module(modulename) except ImportError as e: log.error('sys.path is %s', sys.path) raise e record_type = getattr(module, classname) # load the record result = api.load_record(memory_handler, record_type, memory_address) results = [result] # validate if required validation = None if args.constraints_file: handler = constraints.ConstraintsConfigHandler() my_constraints = handler.read(args.constraints_file.name) validation = api.validate_record(memory_handler, result[0], my_constraints) # output handling ret = None try: ret = get_output(memory_handler, results, args.output) # print output on stdout print ret if args.constraints_file: print 'Validated', validation except Exception as e: log.error(e) finally: if args.interactive: print 'results are local variable "results"' import code code.interact(local=locals()) return
def __init__(self, memory_handler): """ :param memory_handler: IMemoryHandler :return: HeapFinder """ super(LibcHeapFinder, self).__init__(memory_handler) heap_module_name = 'haystack.allocators.libc.ctypes_malloc' self._heap_module = self._memory_handler.get_model().import_module( heap_module_name) self._heap_name = 'malloc_chunk' self._heap_record = getattr(self._heap_module, self._heap_name) parser = constraints.ConstraintsConfigHandler() constraint_filename = os.path.join( os.path.dirname(sys.modules[__name__].__file__), 'libcheap.constraints') log.debug('constraint_filename :%s', constraint_filename) self._constraints = parser.read(constraint_filename) return
def test_preload(self): sys.path.append('test/src/') my_model = self.memory_handler.get_model() ctypes6_gen64 = my_model.import_module("ctypes6_gen64") handler = constraints.ConstraintsConfigHandler() my_constraints = handler.read('test/src/ctypes6.constraints') x64_validator = ctypes6.CTypes6Validator(self.memory_handler, my_constraints, ctypes6_gen64) record_names = ['ctypes6_gen64.%s' % n for n in ctypes6_gen64.__all__] krtr = reversers.KnownRecordTypeReverser(self.memory_handler, record_names, my_constraints) krtr.reverse() ## TODO enforce better constraints, and a dynamic Contraints engine results = krtr.get_search_results() for record_name, addresses in results.items(): print(record_name, len(addresses)) pass
def search_cmdline(args): """ Internal cmdline mojo. """ # get the memory handler adequate for the type requested memory_handler = _get_memory_handler(args) # print output on stdout rtype = _get_output_style(args) # try to load constraints # mapper if args.constraints_file: handler = constraints.ConstraintsConfigHandler() my_constraints = handler.read(args.constraints_file.name) else: my_constraints = None # get the structure name modulename, sep, classname = args.struct_name.rpartition('.') module = memory_handler.get_model().import_module(modulename) struct_type = getattr(module, classname) # do the search results = api.search_record(memory_handler, struct_type, my_constraints, extended_search=args.extended_search) if args.interactive: import code code.interact(local=locals()) # output handling ret = None if rtype == 'string': ret = api.output_to_string(memory_handler, results) elif rtype == 'python': ret = api.output_to_python(memory_handler, results) elif rtype == 'json': ret = api.output_to_json(memory_handler, results) elif rtype == 'pickled': ret = api.output_to_pickle(memory_handler, results) else: raise ValueError('unknown output format') print ret return
def test_config_constraints(self): # the constraints are imposed through config file. parser = constraints.ConstraintsConfigHandler() module_constraints = parser.read('test/src/ctypes6.constraints') self.validator = basicmodel.CTypesRecordConstraintValidator( self.memory_handler, module_constraints) # should be valid. node1 = self.offsets['test2'][0] node2 = self.offsets['test3'][0] # 0xdeadbabe for instance_addr in [node1, node2]: m = self.memory_handler.get_mapping_for_address(instance_addr) node = m.read_struct(instance_addr, self.ctypes_gen64.struct_Node) self.assertTrue(self.validator.is_valid(node)) # should be invalid. items1 = self.offsets['mid_list'][0] items2 = self.offsets['end_list'][0] for instance_addr in [items1, items2]: m = self.memory_handler.get_mapping_for_address(instance_addr) node = m.read_struct(instance_addr, self.ctypes_gen64.struct_Node) self.assertFalse(self.validator.is_valid(node))
def test_search(self): handler = constraints.ConstraintsConfigHandler() my_constraints = handler.read('test/src/ctypes6.constraints') results = haystack.search_record(self.memory_handler, self.node, my_constraints) self.assertEquals(len(results), 2) (node1, offset1), (node2, offset2) = results self.assertEquals(node1.val1, 0xdeadbeef) self.assertEquals(node1.val2, 0xffffffff) self.assertEquals(node2.val1, 0xdeadbabe) self.assertEquals(node2.val2, 0xffffffff) # FIXME there is a circular reference in json. #with self.assertRaises(ValueError): # haystack.output_to_json(self.memory_handler, results) #self.assertEquals(node2s['val1'], 0xdeadbabe) #self.assertEquals(node2s['val2'], 0xffffffff) model = self.memory_handler.get_model() #import code #code.interact(local=locals()) x = api.output_to_pickle(self.memory_handler, results) rest = pickle.loads(x) return
def search_heap(self, memdumpname): # we need a memory dump loader self.memory_handler = dump_loader.load(memdumpname) my_model = self.memory_handler.get_model() module_name = self._init_module_name(self.memory_handler) # import the module with the right arch heap_module = my_model.import_module(module_name) log.debug('the heap module loaded is %s', module_name) # load the constraints constraint_filename = self._init_constraints_filename(heap_module) parser = constraints.ConstraintsConfigHandler() my_constraints = parser.read(constraint_filename) my_searcher = searcher.AnyOffsetRecordSearcher( self.memory_handler, my_constraints, update_cb=partial(self.print_cb, self.memory_handler)) ## DEBUG # DEBUG PEB search #peb = my_model.import_module('haystack.allocators.win32.winxp_32_peb') ##DEBUG heap_record_name = self._init_heap_record_name() heap_struct = getattr(heap_module, heap_record_name) # on ly return first results in each mapping results = [] for mapping in self.memory_handler.get_mappings(): log.debug("looking at %s", mapping) res = my_searcher._search_in(mapping, heap_struct, nb=1, align=0x1000) # DEBUG PEB search #res = my_searcher._search_in(mapping, peb.struct__PEB, nb=1, align=0x1000) if res: # FIXME output_to are stupid #print haystack.output_to_string(memory_handler, res) results.append(res) return results
def test_read(self): parser = constraints.ConstraintsConfigHandler() module_constraints = parser.read('test/structures/good.constraints') config_constraints = module_constraints.get_constraints() for st, stc in config_constraints.items(): log.debug("structure: %s", st) for field, c in stc.items(): log.debug("\t field: %s constraint: %s", field, c) self.assertIn('Struct2', config_constraints.keys()) s2c = config_constraints['Struct2'] self.assertNotIn('fieldC', s2c.keys()) self.assertIn('field0', s2c.keys()) self.assertIn('field1', s2c.keys()) self.assertIn('field2', s2c.keys()) self.assertIn('field3', s2c.keys()) self.assertIn('field4', s2c.keys()) self.assertIn('field5', s2c.keys()) self.assertIn('field6', s2c.keys()) self.assertIn('field7', s2c.keys()) self.assertIn('field8', s2c.keys()) self.assertIn('FiELD9', s2c.keys()) # erroneous. It should be a list. Always. field0 = s2c['field0'] self.assertTrue(isinstance(field0, list)) self.assertEqual(field0, [-2, -3]) field1 = s2c['field1'] self.assertTrue(isinstance(field1, list)) self.assertEqual(1, field1[0].low) self.assertEqual(16, field1[0].high) self.assertEqual('RangeValue', field1[0].__class__.__name__) field2 = s2c['field2'] self.assertTrue(isinstance(field2, list)) self.assertEqual('IgnoreMember', field2[0].__name__) field3 = s2c['field3'] self.assertTrue(isinstance(field3, list)) self.assertEqual(field3, [0, 1]) field4 = s2c['field4'] self.assertTrue(isinstance(field4, list)) # no special character support self.assertEqual('qwklqwfnkl\\x20+++[po-09', field4[0].seq) self.assertEqual('BytesComparable', field4[0].__class__.__name__) field5 = s2c['field5'] self.assertTrue(isinstance(field5, list)) self.assertEqual('NotNullComparable', field5[0].__class__.__name__) field6 = s2c['field6'] self.assertTrue(isinstance(field6, list)) self.assertIn(-1, field6) self.assertIn(1, field6) self.assertIn(constraints.RangeValue(2, 3), field6) self.assertIn(constraints.RangeValue(4, 5), field6) self.assertIn(constraints.PerfectMatch(b'plop'), field6) field7 = s2c['field7'] self.assertTrue(isinstance(field7, list)) self.assertEqual(field7, [-1, 0, 0.0, 1.02]) field8 = s2c['field8'] self.assertTrue(isinstance(field8, list)) self.assertEqual(field8, [0x0, 0x1, 0xff, 0xffeeffee, -0x20])