Skip to content

Commit

Permalink
seemd like a coherent winxp/win7
Browse files Browse the repository at this point in the history
  • Loading branch information
trolldbois committed Jan 7, 2016
1 parent f05988a commit 264703b
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 88 deletions.
81 changes: 75 additions & 6 deletions haystack/allocators/win32/win7heap.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,78 @@ def _heap_entry_to_lfh(self, entry):
return entry._0._0._1._0
return

def collect_all_ucrs(self, heap):
ucrs = set()
for segment in self.get_segment_list(heap):
for ucr in self.get_UCR_segment_list(segment):
ucr_addr = self._utils.get_pointee_address(ucr.Address)
ucr_size = ucr.Size
ucrs.add((ucr_addr, ucr_size))
# UCRList
for ucr in self.HEAP_get_UCRanges_list(heap):
ucr_addr = self._utils.get_pointee_address(ucr.Address)
ucr_size = ucr.Size
ucrs.add((ucr_addr, ucr_size))
return ucrs

def get_UCR_segment_list(self, segment):
"""Returns a list of UCR segments for this segment.
HEAP_SEGMENT.UCRSegmentList is a linked list to UCRs for this segment.
Some may have Size == 0.
"""
if not isinstance(segment, self.win_heap.HEAP_SEGMENT) and not isinstance(segment, self.win_heap.HEAP):
raise TypeError('record should be a heap_segment, not %s' % segment)
# the record at end_segment-0x10 is not actually invalid.
# it is a valid HEAP_UCR_DESCRIPTOR. Most of the time, with a Size of 0.
ucrs = list()
for ucr in self.iterate_list_from_field(segment, 'UCRSegmentList'):
ucr_struct_addr = ucr._orig_address_
ucr_addr = self._utils.get_pointee_address(ucr.Address)
# UCR.Size are not chunks sizes. NOT *8
log.debug("Segment.UCRSegmentList: 0x%0.8x addr: 0x%0.8x size: 0x%0.5x" % (
ucr_struct_addr, ucr_addr, ucr.Size))
ucrs.append(ucr)
return ucrs

def HEAP_get_UCRanges_list(self, heap):
"""
win7
Returns a list of available UCR segments for this heap.
HEAP.UCRList is a linked list to all UCRSegments
"""
if not isinstance(heap, self.win_heap.HEAP):
raise TypeError('record should be a heap, not %s' % heap)
ucrs = list()
for ucr in self.iterate_list_from_field(heap, 'UCRList'):
ucr_struct_addr = ucr._orig_address_
ucr_addr = self._utils.get_pointee_address(ucr.Address)
# UCR.Size are not chunks sizes. NOT *8
log.debug("Heap.UCRList: 0x%0.8x addr: 0x%0.8x size: 0x%0.5x" % (
ucr_struct_addr, ucr_addr, ucr.Size))
ucrs.append(ucr)
return ucrs

def UNUSED_HEAP_get_UCRange_segment_list(self, record):
"""
Returns a list of uncommited segment for this UCR.
HEAP.UCRList->SegmentEntry is a linked list to all UCRSegments
"""
expected_type = 'HEAP_UCR_DESCRIPTOR'
if expected_type not in str(type(record)):
raise TypeError('record %s should be of type %s' % (record, expected_type))
entries = list()
for entry in self.iterate_list_from_field(record, 'SegmentEntry'):
entry_struct_addr = entry._orig_address_
entry_addr = self._utils.get_pointee_address(entry.BaseAddress)
first = entry.FirstEntry.value
last = entry.LastValidEntry.value
size = last - first
log.debug("Heap.UCRList.SegmentEntry: 0x%0.8x addr: 0x%0.8x size: 0x%0.5x" % (
entry_struct_addr, entry_addr, size))
entries.append(entry)
return entries

def _get_LFH_SubSegment_from_SubSegmentZones(self, lfh_heap):
"""
Expand Down Expand Up @@ -230,8 +302,9 @@ def get_lfh_subsegment(self, lfh_block):
# LFH_BLOCK_ZONE contains a list field to other LFH_BLOCK_ZONE, a FreePointer and a limit
end = lfh_block_addr + self._ctypes.sizeof(lfh_block)
fp = self._utils.get_pointee_address(lfh_block.FreePointer)
if fp < lfh_block_addr:
log.error('got a bad FreePointer value 0x%x' % fp)
# that is a sentinels.
if fp == (0x10 + self._ctypes.sizeof(lfh_block)):
log.debug('Got a special FreePointer value 0x%x' % fp)
return None, None
# Segments are running from after the lfh_block to the FreePointer
subseg_size = self._ctypes.sizeof(self.win_heap.struct__HEAP_SUBSEGMENT)
Expand Down Expand Up @@ -379,10 +452,6 @@ def print_segments_analysis(self, heap, walker, ucrs):
occupied_res2 = self.count_by_segment(segments, walker.get_user_allocations(), overhead_size)
free_res2 = self.count_by_segment(segments, walker.get_free_chunks(), overhead_size)
print "\tSegmentList: %d/%d" % (len(segments), nb_segments)
# print ".SegmentList.Flink", hex(heap.SegmentList.Flink.value)
# print ".SegmentList.Blink", hex(heap.SegmentList.Blink.value)
# print ".SegmentListEntry.Flink", hex(heap.SegmentListEntry.Flink.value)
# print ".SegmentListEntry.Blink", hex(heap.SegmentListEntry.Blink.value)
for segment in segments:
p_segment = winheap.Segment(self._memory_handler, segment)
p_segment.set_ucr(ucr_list)
Expand Down
103 changes: 27 additions & 76 deletions haystack/allocators/win32/winheap.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,84 +158,28 @@ def HEAP_get_virtual_allocated_blocks_list(self, record):
# some heap UCR will not be listed in segment ucr and vice-versa
# get_ucr == get_ucr_from_heap + get_ucr_from_segment

def get_UCR_segment_list(self, record):
"""Returns a list of UCR segments for this segment.
HEAP_SEGMENT.UCRSegmentList is a linked list to UCRs for this segment.
Some may have Size == 0.
"""
# the record at end_segment-0x10 is not actually invalid.
# it is a valid HEAP_UCR_DESCRIPTOR. Most of the time, with a Size of 0.
ucrs = list()
for ucr in self.iterate_list_from_field(record, 'UCRSegmentList'):
ucr_struct_addr = ucr._orig_address_
ucr_addr = self._utils.get_pointee_address(ucr.Address)
# UCR.Size are not chunks sizes. NOT *8
log.debug("Segment.UCRSegmentList: 0x%0.8x addr: 0x%0.8x size: 0x%0.5x" % (
ucr_struct_addr, ucr_addr, ucr.Size))
ucrs.append(ucr)
return ucrs

def HEAP_get_UCRanges_list(self, record):
"""
win7
Returns a list of available UCR segments for this heap.
HEAP.UCRList is a linked list to all UCRSegments
"""
# TODO: exclude UCR segment from valid pointer values in _memory_handler.
ucrs = list()
for ucr in self.iterate_list_from_field(record, 'UCRList'):
ucr_struct_addr = ucr._orig_address_
ucr_addr = self._utils.get_pointee_address(ucr.Address)
# UCR.Size are not chunks sizes. NOT *8
log.debug("Heap.UCRList: 0x%0.8x addr: 0x%0.8x size: 0x%0.5x" % (
ucr_struct_addr, ucr_addr, ucr.Size))
ucrs.append(ucr)
return ucrs

def UNUSED_HEAP_get_UCRange_segment_list(self, record):
"""
Returns a list of uncommited segment for this UCR.
HEAP.UCRList->SegmentEntry is a linked list to all UCRSegments
def collect_all_ucrs(self, heap):
raise NotImplementedError

"""
expected_type = 'HEAP_UCR_DESCRIPTOR'
if expected_type not in str(type(record)):
raise TypeError('record %s should be of type %s' % (record, expected_type))
entries = list()
for entry in self.iterate_list_from_field(record, 'SegmentEntry'):
entry_struct_addr = entry._orig_address_
entry_addr = self._utils.get_pointee_address(entry.BaseAddress)
first = entry.FirstEntry.value
last = entry.LastValidEntry.value
size = last - first
log.debug("Heap.UCRList.SegmentEntry: 0x%0.8x addr: 0x%0.8x size: 0x%0.5x" % (
entry_struct_addr, entry_addr, size))
entries.append(entry)
return entries

def get_backend_chunks(self, record):
def get_backend_chunks(self, heap):
"""
Returns a list of tuple(address,size) for all chunks in
the backend allocator.
"""
allocated = set()
free = set()
# FIXME look at segment.LastEntryInSegment
for segment in self.get_segment_list(record):
ucrs = self.collect_all_ucrs(heap)
skiplist = dict()
# common UCR management for XP and win7
for ucr_addr, ucr_size in ucrs:
skiplist[ucr_addr] = ucr_size
log.debug('adding skiplist from %x to %x', ucr_addr, ucr_addr+ucr_size)
log.debug('skiplist has %d items', len(skiplist))
# now parse all segment
for segment in self.get_segment_list(heap):
first_addr = self._utils.get_pointee_address(segment.FirstEntry)
last_addr = self._utils.get_pointee_address(segment.LastValidEntry)
# create the skip list for each segment.
skiplist = dict()
# FIXME, common UCR management for XP and win7
for ucr in self.get_UCR_segment_list(segment):
ucr_addr = self._utils.get_pointee_address(ucr.Address)
# UCR.Size are not chunks sizes. NOT *8
skiplist[ucr_addr] = ucr.Size
log.debug('adding skiplist from %x to %x', ucr_addr, ucr_addr+ucr.Size)
#
log.debug('skiplist has %d items', len(skiplist))
_allocated,_free = self._iterate_chunk_list(record, first_addr, last_addr, skiplist)
_allocated,_free = self._iterate_chunk_list(heap, first_addr, last_addr, skiplist)
allocated |= _allocated
free |= _free
return allocated, free
Expand All @@ -262,19 +206,26 @@ def _iterate_chunk_list(self, heap, first_addr, last_addr, skiplist):
self._memory_handler.keepRef(chunk_header, self.win_heap.HEAP_ENTRY, chunk_addr)
# FIXME what is this hack
chunk_header._orig_address_ = chunk_addr
if heap.EncodeFlagMask: # heap.EncodeFlagMask
if hasattr(heap, 'EncodeFlagMask'): # heap.EncodeFlagMask
chunk_header = self.HEAP_ENTRY_decode(chunk_header, heap)
chunk_header = self._heap_entry_to_size(chunk_header)
# test if chunk is allocated or free
if (chunk_header.Flags & 1) == 1:
allocated.add((chunk_addr, chunk_header.Size * self._word_size_x2))
# test if chunk is allocated or free
flags = chunk_header.Flags
size = chunk_header.Size
else:
if chunk_header.Size == 0:
# winxp
flags = chunk_header._0._1.Flags
size = chunk_header._0._0.Size
if (flags & 1) == 1:
allocated.add((chunk_addr, size * self._word_size_x2))
else:
if size == 0:
log.warning("Null sized free chunk at 0x%0.8x - exiting", chunk_addr)
break
free.add((chunk_addr, chunk_header.Size * self._word_size_x2))
free.add((chunk_addr, size * self._word_size_x2))
pass
chunk_addr += chunk_header.Size * self._word_size_x2

chunk_addr += size * self._word_size_x2
return allocated, free

def get_lookaside_chunks(self, record):
Expand Down
42 changes: 36 additions & 6 deletions haystack/allocators/win32/winxpheap.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,13 +375,33 @@ def HEAP_get_freelists(self, record):
def HEAP_ENTRY_decode(self, chunk_header, heap):
return chunk_header

def collect_all_ucrs(self, heap):
ucrs = set()
# UnCommittedRanges
for segment in self.get_segment_list(heap):
for ucr in self.SEGMENT_get_UCR_list(segment):
ucr_addr = self._utils.get_pointee_address(ucr.Address)
ucr_size = ucr.Size
ucrs.add((ucr_addr, ucr_size))
# UCRSegments
for ucr in self.get_UCR_segment_list(heap):
ucr_addr = self._utils.get_pointee_address(ucr.Address)
ucr_size = ucr.Size
ucrs.add((ucr_addr, ucr_size))
# UnusedUnCommittedRanges
for ucr in self.HEAP_get_UCRanges_list(heap):
ucr_addr = self._utils.get_pointee_address(ucr.Address)
ucr_size = ucr.Size
ucrs.add((ucr_addr, ucr_size))
return ucrs

def get_UCR_segment_list(self, record):
"""Returns a list of UCR segments for this segment.
HEAP.UCRSegments is a linked list to UCRs for this segment.
Some may have Size == 0.
"""
if not isinstance(record, self.win_heap.struct__HEAP):
raise TypeError('record should be a heap')
raise TypeError('record should be a heap, not %s' % record)
ucrs = []
for ucr in self.iterate_list_from_pointer_field(record.UCRSegments, 'Next'):
ucr_struct_addr = ucr._orig_address_
Expand Down Expand Up @@ -433,7 +453,7 @@ def SEGMENT_get_UCR_list(self, record):
ucrs.append(ucr)
return ucrs

def get_backend_chunks(self, record):
def get_backend_chunks2(self, record):
"""
Returns a list of tuple(address,size) for all chunks in
the backend allocator.
Expand Down Expand Up @@ -503,6 +523,8 @@ def print_heap_analysis_details(self, heap):
# winXP
# Heap's unuseduncommitted ranges
# Heap.UnusedUnCommittedRanges
# size & space calculated from heap info
print ' Backend:'
ucrs = self.HEAP_get_UCRanges_list(heap)
ucr_list = winheap.UCR_List(ucrs)
print '\tUnused UCR: %d' % (len(ucrs))
Expand All @@ -513,12 +535,15 @@ def print_heap_analysis_details(self, heap):
for ucr_segment in ucrsegments:
print "\t\t\t\tUCRSegment: 0x%0.8x-0x%0.8x size:0x%x" % (ucr_segment.Address, ucr_segment.Address+ucr_segment.Size, ucr_segment.Size)
# print "\t\t\t\t.Segment.Next", hex(ucr_segment.Next.value)
# Virtual Allocations
vallocs = self.HEAP_get_virtual_allocated_blocks_list(heap)
print '\tVAllocations: %d' % len(vallocs)
for addr, c_size, r_size in vallocs:
diff = '' if c_size == r_size else '!!'
# print "vallocBlock: @0x%0.8x commit: 0x%x reserved: 0x%x" % (
print "\t\t%svalloc: 0x%0.8x-0x%0.8x size:0x%x requested:0x%x " % (diff, addr, addr+c_size, c_size, r_size)
return ucrs

def print_frontend_analysis_details(self, heap):
# get_lookaside_chunks
return

def print_segments_analysis(self, heap, walker, ucrs):

# heap is a segment
Expand Down Expand Up @@ -548,3 +573,8 @@ def print_segments_analysis(self, heap, walker, ucrs):
print "\t\t\t\tUCRSegment: 0x%0.8x-0x%0.8x size:0x%x" % (ucr_segment.Address, ucr_segment.Address+ucr_segment.Size, ucr_segment.Size)
#print "\t\t\t\t.Segment.Next", hex(ucr_segment.Next.value)
#

def print_frontend_analysis_details(self, heap):
# get_lookaside_chunks
return

0 comments on commit 264703b

Please sign in to comment.