Package Libs :: Module libheap
[hide private]
[frames] | no frames]

Source Code for Module Libs.libheap

   1  #!/usr/bin/env python 
   2  """ 
   3  Immunity Heap API for Immunity Debugger 
   4   
   5  (c) Immunity, Inc. 2004-2006 
   6   
   7   
   8  U{Immunity Inc.<http://www.immunityinc.com>} Debugger Heap Library for python 
   9   
  10   
  11  """ 
  12   
  13  __VERSION__ = '1.3' 
  14   
  15  import immutils 
  16  import struct 
  17  import string 
  18  from UserList import UserList 
  19  HEAP_MAX_FREELIST = 0x80 
  20  DEBUG = False 
  21   
  22   
  23   
24 -class PHeap:
25 - def __init__(self, imm, heapddr = 0, restore = False):
26 """ 27 Windows 32 Heap Class 28 29 @rtype: PHEAP object 30 """ 31 self.imm = imm 32 self.address = heapddr 33 self.chunks = [] 34 self.restore = restore 35 self.Segments = [] 36 self.HeapCache = None 37 self.Lookaddr = None 38 self.Lookaside = None 39 40 if heapddr: 41 self._grabHeap()
42
43 - def _grabHeap(self):
44 try: 45 heaps = self.imm.readMemory( self.address, 0x588 ) 46 except WindowsError, msg: 47 raise Exception, "Failed to get heap at address : 0x%08x" % heapaddr 48 49 index = 0x8 50 (self.Signature, self.Flags, self.ForceFlags, self.VirtualMemoryThreshold,\ 51 self.SegmentReserve, self.SegmentCommit, self.DeCommitFreeBlockThreshold, self.DeCommitTotalBlockThreshold,\ 52 self.TotalFreeSize, self.MaximumAllocationSize, self.ProcessHeapListIndex, self.HeaderValidateLength,\ 53 self.HeaderValidateCopy,self.NextAvailableTagIndex, self.MaximumTagIndex, self.TagEntries, \ 54 self.UCRSegments, self.UnusedUnCommittedRanges, self.AlignRound, self.AlignMask) =\ 55 struct.unpack("LLLLLLLLLLHHLHHLLLLL", heaps[ index : index + (0x50-8) ]) 56 57 index+= 0x50-8 58 self.VirtualAllocedBlock = struct.unpack("LL", heaps[ index : index + 8 ]) 59 index+=8 60 self._Segments = struct.unpack("L" * 64, heaps[ index: index+ 64*4 ]) 61 index+=64*4 62 self.FreeListInUseLong = struct.unpack("LLLL" , heaps[ index : index + 16 ]) 63 index+=16 64 (self.FreeListInUseTerminate,self.AllocatorBackTraceIndex) = struct.unpack("HH", heaps[ index : index + 4 ]) 65 index+=4 66 (self.Reserved1, self.LargeBlocksIndex)= struct.unpack("LL", heaps[ index : index + 8 ]) 67 index+=8 68 if self.LargeBlocksIndex: 69 self.HeapCache = HeapCache( self.imm, self.LargeBlocksIndex) 70 71 self.PseudoTagEntries= struct.unpack("L", heaps[ index : index + 4]) 72 index+=4 73 self.FreeList=[] 74 75 # Getting the FreeList 76 for a in range(0, 128): 77 free_entry = [] 78 # Previous and Next Chunk of the head of the double linked list 79 (next, prev) = struct.unpack("LL", heaps[ index + a*8 : index + a*8 + 8 ]) 80 81 free_entry.append((self.address + index+ a * 8, prev, next) ) 82 base_entry = self.address + index + a * 8 83 84 # Loop over the Double Linked List until next == to the begging of the list. 85 tmp = 0 86 while next != base_entry: 87 if tmp == next: 88 break 89 tmp = next 90 try: 91 (next, prev) = struct.unpack("LL", self.imm.readMemory(next, 0x8)) 92 except: 93 break 94 95 free_entry.append( (tmp, prev,next) ) 96 97 self.FreeList.append(free_entry) 98 99 index+=256*4 100 (self.LockVariable, self.CommitRoutine, self.Lookaddr, self.LookasideLockCount)=\ 101 struct.unpack("LLLL", heaps[index:index+16]) 102 103 if self.Lookaddr: 104 self.Lookaside = PHeapLookaside(self.imm, self.Lookaddr) 105 106 # the first segment is the heap on the base address (the 2nd chunk) 107 #self.Segments. 108 for a in range(0, 64): 109 if self._Segments[a] == 0x0: 110 break 111 s = Segment( self.imm, self._Segments[a] ) 112 self.Segments.append( s ) 113 #imm.log("Segment[%d]: 0x%08x" % (a, self.Segments[a])) 114 # BaseAddress 115 if self.restore: 116 self.getRestoredChunks( s.BaseAddress ) 117 else: 118 self.getChunks( s.BaseAddress ) 119 for idx in s.Pages: 120 self.imm.log("> 0x%08x" % idx) 121 if self.restore: 122 self.getRestoredChunks( idx ) 123 else: 124 self.getChunks( idx )
125
126 - def printLookaside(self, uselog=None):
127 if not self.Lookaside: 128 return None 129 130 if not uselog: 131 uselog = imm.log 132 133 for ndx in range(0, len(self.Lookaside)): 134 entry = self.Lookaside[ndx] 135 if not entry.isEmpty(): 136 137 uselog("(%02x) Depth: %d: MaxDepth: %d FreeMiss: %d" % (ndx, entry.Depth, entry.MaxDepth, entry.FreeMiss), address = entry.addr) 138 for a in entry.getList(): 139 uselog(" > 0x%08x (0x%03x)" % (a, ndx * 8), address = a)
140
141 - def decimal2binary(self, num, swap = True):
142 if num == 0: 143 return '0'*32 144 if num < 0 : 145 return '' 146 ret = [] 147 # while num > 0: 148 for a in range(32,-1, -1): 149 ret.append( str(num&0x1) ) 150 num = num >> 1 151 if not swap: 152 ret.reverse() 153 return "".join(ret)
154
155 - def printHeapCache(self, uselog=None):
156 ret = [] 157 uselog("HeapCache Bitmask:") 158 for a in range(0, self.HeapCache.NumBuckets/32/4): 159 ret.append(self.decimal2binary(self.HeapCache.Bitmask[a])) 160 #uselog( "%s" % 161 if a%2: 162 uselog( " %s" % str(" ".join(ret) ) ) 163 ret = [] 164 165 for a in range(0, self.HeapCache.NumBuckets): 166 if self.HeapCache.Buckets[a]: 167 uselog("HEAP_CACHE[%04x] = 0x%08x" % (a+0x80, self.HeapCache.Buckets[a]), address = self.HeapCache.Buckets[a])
168
169 - def printFreeListInUse(self, uselog=None):
170 """ 171 Print the Heap's FreeListInUse bitmask 172 173 @type uselog: Log Function 174 @param uselog: (Optional, Def: Log Window) Log function that display the information 175 """ 176 tbl= ["FreeListInUse %s %s"% (self.decimal2binary(self.FreeListInUseLong[0]), self.decimal2binary(self.FreeListInUseLong[1])),\ 177 " %s %s" % (self.decimal2binary(self.FreeListInUseLong[2]), self.decimal2binary(self.FreeListInUseLong[3]))] 178 if uselog: 179 for a in tbl: 180 uselog(a) 181 return tbl
182
183 - def printFreeList(self, uselog = None):
184 """ 185 Print the Heap's FreeList 186 187 @type uselog: Log Function 188 @param uselog: (Optional, Def: Log Window) Log function that display the information 189 """ 190 log = self.imm.log 191 if uselog: 192 log = uselog 193 for a in range(0, 128): 194 entry= self.FreeList[a] 195 e=entry[0] 196 197 log("[%03x] 0x%08x -> [ 0x%08x | 0x%08x ] " % (a, e[0], e[1], e[2]), address = e[0]) 198 for e in entry[1:]: 199 try: 200 sz = self.get_chunk( e[0] - 8 ).size 201 except: 202 sz = 0 203 log(" 0x%08x -> [ 0x%08x | 0x%08x ] (%08x)" % (e[0], e[1], e[2], sz), address= e[0]) 204 return 0x0
205 206 # Get Chunnks restored
207 - def getRestoredChunks(self, address):
208 """ 209 Enumerate Chunks of the current heap using a restore heap 210 211 @type address: DWORD 212 @param address: Address where to start getting chunks 213 214 @rtype: List of win32heapchunks 215 @return: Chunks 216 """ 217 218 imm = self.imm 219 220 oldheap = imm.getKnowledge("saved_heap_%08x" % self.address) #retriving the heap 221 if not oldheap: 222 imm.log("Coudln't use restore mode: No saved Heap") 223 return self.getChunks(address) 224 225 ptr = address 226 # null chunk 227 backchunk = self.get_chunk(imm, ptr, self.address) 228 229 backchunk.size = backchunk.psize 230 backchunk.usize = backchunk.upsize 231 232 while 1: 233 234 try: 235 c = self.get_chunk(imm, ptr, self.address) 236 except: 237 return self.chunks 238 239 #ptr+= c.size * 8 240 next = ptr + c.usize 241 242 try: 243 sizes = imm.readLong( next ) 244 previous = (sizes>>16) & 0xffff 245 except Exception: 246 previous = 0 # unable to read 247 248 # When to restore? 249 # o Chunk size is zero 250 # o Chunk previous size is zero 251 # o When Size is different from next chunk previous size 252 # o Next chunk previous size is zero (means, readLong fails) and the chunk is not a top chunk 253 # o When the size of the backward chunk is different for the chunk Size 254 if (not c.size) or (c.size != previous and not c.istop()) or (not previous and not c.istop()) or (backchunk.size != c.psize) : 255 restoredchunk = oldheap.findChunkByAddress(ptr) 256 257 if restoredchunk: 258 c = restoredchunk 259 c.setRestored() 260 next = ptr + c.usize 261 ptr = next 262 self.chunks.append(c) 263 backchunk = c 264 265 266 if c.istop() or c.size == 0: 267 break 268 269 backchunk = c 270 271 return self.chunks
272
273 - def findChunkByAddress(self, addr):
274 """ 275 Find a Chunks by its address 276 277 @type address: DWORD 278 @param address: Address to search for 279 280 @rtype: win32heapchunks 281 @return: Chunk 282 """ 283 284 for a in self.chunks: 285 if a.addr == addr: 286 return a 287 return None
288
289 - def getChunks(self, address, size = 0xffffffffL):
290 """ 291 Enumerate Chunks of the current heap 292 293 @type address: DWORD 294 @param address: Address where to start getting chunks 295 296 @type size: DWORD 297 @param size: (Optional, Def: All) Amount of chunks 298 299 @rtype: List of win32heapchunks 300 @return: Chunks 301 """ 302 imm = self.imm 303 304 ptr = address 305 306 while size: 307 308 try: 309 c = self.get_chunk( ptr ) 310 except Exception, msg: 311 imm.log("Failed to grab chunks> " + str(msg) ) 312 return self.chunks 313 314 self.chunks.append(c) 315 316 #c.printchunk() 317 ptr+= c.usize 318 if c.istop() or c.size == 0: 319 break 320 size -= 1 321 322 return self.chunks
323
324 - def get_chunk(self, addr):
325 return win32heapchunk(self.imm, addr, self)
326
327 -class Segment:
328 - def __init__(self, imm, addr):
329 self.address = addr 330 addr += 8 # AVOID THE ENTRY ITSELF 331 imm.log(" segment 0x%08x " % addr) 332 mem = imm.readMemory(addr, 0x34) 333 334 (self.Signature, self.Flags, self.Heap, self.LargestUnCommitedRange, self.BaseAddress,\ 335 self.NumberOfPages, self.FirstEntry, self.LastValidEntry, self.NumberOfUnCommittedPages,\ 336 self.NumberOfUnCommittedRanges, self.UnCommittedRanges, self.AllocatorBackTraceIndex,\ 337 self.Reserved, self.LastEntryInSegment) = struct.unpack("LLLLLLLLLLLHHL", mem) 338 if DEBUG: 339 imm.log("SEGMENT: 0x%08x Sig: %x" % (self.address, self.Signature), address = self.address ) 340 imm.log("Heap: %08x LargetUncommit %08x Base: %08x" % (self.Heap, self.LargestUnCommitedRange, self.BaseAddress)) 341 imm.log("NumberOfPages %08x FirstEntry: %08x LastValid: %08x" % (self.NumberOfPages, self.FirstEntry, self.LastValidEntry)) 342 imm.log("Uncommited: %08x" % self.UnCommittedRanges) 343 self.Pages = [] 344 if self.UnCommittedRanges: 345 i = 0 346 addr = self.UnCommittedRanges 347 while addr != 0: 348 mem = imm.readMemory( addr, 0x10 ) 349 ( C_Next, C_Addr, C_Size, C_Filler) = struct.unpack( "LLLL", mem ) 350 if DEBUG: 351 imm.log( ">> Memory: 0x%08x Address: 0x%08x (a: %08x) Size: %x" % ( addr, C_Next, C_Addr,C_Size) ) 352 self.Pages.append( C_Addr + C_Size ) 353 addr = C_Next
354
355 -class VistaPHeap(PHeap):
356 LFH = None
357 - def __init__(self, imm, heapddr = 0, restore = False):
358 PHeap.__init__(self, imm, heapddr, restore)
359
360 - def _grabHeap(self):
361 try: 362 heapmem = self.imm.readMemory( self.address + 8 , 0x120 ) 363 except WindowsError, msg: 364 raise Exception, "Failed to get heap at address : 0x%08x" % heapaddr 365 index = 8 366 (self.SegmentSignature, self.SegmentFlags, self.SegmentListEntry_Flink, self.SegmentListEntry_Blink, self.Heap, self.BaseAddress, self.NumberOfPages, self.FirstEntry, self.LastValidEntry, self.NumberofUncommitedPages, self.NumberofUncommitedRanges, self.SegmentAllocatorBackTraceIndex, self.Reserved, self.UCRSegmentList_Flink, self.UCRSegmentList_Blink, self.Flags, self.ForceFlags, self.CompatibilityFlags, self.EncodeFlagMask, self.EncodingKey, self.EncodingKey2, self.PointerKey, self.Interceptor_debug, self.VirtualMemoryThreshold, self.Signature, self.SegmentReserve, self.SegmentCommit, self.DeCommitThresholdBlock, self.DeCommitThresholdTotal, self.TotalFreeSize, self.MaxAllocationSize, self.ProcessHeapsListIndex, self.HeaderValidateLength, self.HeaderValidateCopy, self.NextAvailableTagIndex, self.MaximumTagIndex, self.TagEntries, self.UCRList_Flink, self.UCRList_Blink, self.AlignRound, self.AlignMask, self.VirtualAlloc_Flink, self.VirtualAlloc_Blink, self.SegmentList_Flink, self.SegmentList_Blink, self.AllocatorBackTraceIndex, self.NonDedicatedListLenght, self.BlocksIndex, self.UCRIndex, self.PseudoTagEntries, self.FreeList_Flink, self.FreeList_Blink, self.LockVariable, self.CommitRoutine, self.FrontEndHeap, self.FrontHeapLockCount, self.FrontEndHeapType, self.TotalMemoryReserved, self.TotalMemoryCommited, self.TotalMemoryLargeUCR, self.TotalSizeInVirtualBlocks, self.TotalSegments, self.TotalUCRs, self.CommitOps, self.DecommitOps, self.LockAcquires, self.LockCollisions, self.CommitRate, self.DeCommitRate, self.CommitFailures, self.InBlockCommitFailures, self.CompactHeapCalls, self.CompactedUCRs, self.InBlockDecommits, self.InBlockDecommitSize, self.TunningParameters) = struct.unpack("L" * 11 + "HH" + "L" *18 + "HHLHH" + "L" * 19 + "HH" + "L" * 19, heapmem) 367 head = self.address #+0x10 368 addr = self.SegmentListEntry_Flink 369 self.Segments.append( VistaSegment( self.imm, self.address) ) 370 self.getChunks( self.address ) 371 372 if addr !=0 : 373 while head != (addr& ~0xff): 374 self.Segments.append( VistaSegment(self.imm, addr - 0x10 ) ) 375 self.getChunks( addr & ~0xff ) 376 addr = self.imm.readLong( addr ) 377 else: 378 self.imm.log("Error: HEAP_SEGMENT address is 0x00000000") 379 380 self.getBlocks( self.BlocksIndex ) 381 if self.FrontEndHeap: 382 if self.imm.isWin7: 383 self.LFH = Win7LFHeap(self.imm, self.FrontEndHeap) 384 else: 385 self.LFH = LFHeap( self.imm, self.FrontEndHeap )
386 387
388 - def getBlocks(self, startaddr):
389 self.blocks = [] 390 addr = startaddr 391 392 while addr: 393 block = Blocks( self.imm, addr ) 394 self.blocks.append( block ) 395 block.FreeList=[] 396 397 #calculate the number of freelists 398 num_of_freelists = block.ArraySize - block.BaseIndex 399 400 memory = self.imm.readMemory( block.ListHints, num_of_freelists * 8 ) 401 #memory = self.imm.readMemory( block.Buckets, 0x80*8 ) 402 403 if block.ListsInUseUlong: 404 block.setFreeListInUse( struct.unpack("LLLL", self.imm.readMemory( block.ListsInUseUlong, 4*4 )) ) 405 406 # Getting the FreeList 407 for a in range(0, num_of_freelists): 408 free_entry = [] 409 # Previous and Next Chunk of the head of the double linked list 410 (fwlink, heap_bucket) = struct.unpack("LL", memory[a *8 : a *8 + 8] ) 411 if fwlink: 412 try: 413 (next, prev) = struct.unpack("LL", self.imm.readMemory( fwlink, 8) ) 414 except: 415 next, prev = (0,0) 416 self.imm.log("Error with 0x%x" % fwlink) 417 free_entry.append( (fwlink, next, prev) ) 418 base_entry = fwlink 419 420 while next and next != base_entry: 421 tmp = next 422 try: 423 chunk = win32vistaheapchunk( self.imm, next - 8, self ) 424 except Exception: 425 break 426 self.imm.log("size: %d addr: 0x%08x 0x%08x 0x%08x" % (chunk.size, next-8, chunk.nextchunk, chunk.prevchunk)) 427 428 if a == 127: 429 if chunk.size <= a: 430 break 431 else: 432 if chunk.size != a: 433 break 434 435 next = chunk.nextchunk 436 free_entry.append( (tmp, chunk.nextchunk, chunk.prevchunk) ) 437 438 else: 439 free_entry = [ (fwlink, 0x0, 0x0) ] 440 441 #if heap_bucket & 1: 442 # bucket = self.getBucket( heap_bucket - 1 ) 443 block.FreeList.append(free_entry) 444 445 addr = block.ExtendedLookup
446
447 - def get_chunk(self, addr):
448 return win32vistaheapchunk(self.imm, addr, self)
449
450 - def printFreeList(self, uselog = None):
451 """ 452 Print the Heap's FreeList 453 454 @type uselog: Log Function 455 @param uselog: (Optional, Def: Log Window) Log function that display the information 456 """ 457 log = self.imm.log 458 if uselog: 459 log = uselog 460 for block in self.blocks: 461 f = block.FreeListInUse 462 log("** Block 0x%08x BaseIndex: %d ArraySize: %d ExtraItem: %d **" % ( block.address, block.BaseIndex, block.ArraySize, block.ExtraItem ) ) 463 if DEBUG: 464 log("** ListInUse: 0x%08x" % block.ListsInUseUlong) 465 if f: 466 log("FreeListInUse: %s %s" % (immutils.decimal2binary(f[0]),\ 467 immutils.decimal2binary(f[1]) ) ) 468 log(" %s %s" % (immutils.decimal2binary(f[2]),\ 469 immutils.decimal2binary(f[3]) ) ) 470 471 for a in range(0, 128): 472 entry= block.FreeList[a] 473 e=entry[0] 474 if e[0]: 475 log("[%03d] 0x%08x -> [ 0x%08x | 0x%08x ] " % (a, e[0], e[1], e[2]), address = e[0]) 476 for e in entry[1:]: 477 log(" 0x%08x -> [ 0x%08x | 0x%08x ] " % (e[0], e[1], e[2]), address= e[0]) 478 return 0x0
479
480 -class VistaSegment:
481 - def __init__(self, imm, addr):
482 483 self.address = addr 484 addr += 8 # AVOID THE ENTRY ITSELF 485 mem = imm.readMemory(addr+8, 0x38) 486 487 (self.SegmentSignature, self.SegmentFlags, self.SegmentListEntry_Flink, self.SegmentListEntry_Blink,\ 488 self.Heap, self.BaseAddress, self.NumberOfPages, self.FirstEntry, self.LastValidEntry,\ 489 self.NumberofUncommitedPages, self.NumberofUncommitedRanges, self.SegmentAllocatorBackTraceIndex,\ 490 self.Reserved,self.UCRSegmentList_Flink,self.UCRSegmentList_Blink)=\ 491 struct.unpack( "L" * 11 + "HH" + "L" *2, mem) 492 self.Entry = win32vistaheapchunk(imm, addr)
493 494 495
496 -class Win7LFHeap:
497 - def __init__(self, imm, addr):
498 mem = imm.readMemory( addr, 0x310 ) 499 if not mem: 500 raise Exception, "Can't read Low Fragmentation Heap at 0x%08x" % addr 501 index = 0 502 self.address = addr 503 imm.log("Low Fragmentation Heap: 0x%08x" % addr) 504 (self.Lock, self.field_4, self.field_8, self.field_c,\ 505 self.field_10, field_14, self.SubSegmentZone_Flink, 506 self.SubSegmentZone_Blink, self.ZoneBlockSize,\ 507 self.Heap, self.SegmentChange, self.SegmentCreate,\ 508 self.SegmentInsertInFree, self.SegmentDelete, self.CacheAllocs,\ 509 self.CacheFrees, self.SizeInCache, self.Padding) = struct.unpack("L" * 0x12, mem[ index : index +0x48 ]) 510 511 index += 0x48 512 513 #get the heapbucket run info 514 self.RunInfo = HeapBucketRunInfo(addr + index, mem[index : index + 8]) 515 index += 0x8 516 517 self.UserBlockCache = [] 518 for a in range(0,12): 519 umc = UserMemoryCache( addr + index, mem[ index : index + 0x10] ) 520 index+= 0x10 521 self.UserBlockCache.append( umc ) 522 self.Buckets = [] 523 for a in range(0, 128): 524 entry = mem[ index : index + 4 ] 525 b = Bucket( addr + index, entry) 526 index = index + 4 527 self.Buckets.append( b ) 528 529 self.LocalData = LocalData(imm, addr + index )
530
531 -class HeapBucketRunInfo:
532 - def __init__(self, addr, mem):
533 self.address = addr 534 (self.Bucket, self.RunLength) = struct.unpack("LL", mem[ 0 : 0x8 ])
535 536
537 -class LFHeap:
538 - def __init__(self, imm, addr):
539 mem = imm.readMemory( addr, 0x300 ) 540 if not mem: 541 raise Exception, "Can't read Low Fragmentation Heap at 0x%08x" % addr 542 index = 0 543 self.address = addr 544 imm.log("Low Fragmented Heap: 0x%08x" % addr) 545 (self.Lock, self.field_4, self.field_8, self.field_c,\ 546 self.field_10, field_14, self.SubSegmentZone_Flink, 547 self.SubSegmentZone_Blink, self.ZoneBlockSize,\ 548 self.Heap, self.SegmentChange, self.SegmentCreate,\ 549 self.SegmentInsertInFree, self.SegmentDelete, self.CacheAllocs,\ 550 self.CacheFrees) = struct.unpack("L" * 0x10, mem[ index : index +0x40 ]) 551 index += 0x40 552 self.UserBlockCache = [] 553 for a in range(0,12): 554 umc = UserMemoryCache( addr + index, mem[ index : index + 0x10] ) 555 index+= 0x10 556 self.UserBlockCache.append( umc ) 557 self.Buckets = [] 558 for a in range(0, 128): 559 entry = mem[ index : index + 4 ] 560 b = Bucket( addr + index, entry) 561 index = index + 4 562 self.Buckets.append( b ) 563 564 self.LocalData = LocalData(imm, addr + index )
565
566 -class LocalData:
567 - def __init__(self, imm, addr):
568 self.address = addr 569 570 mem = imm.readMemory( addr, 0x18 + 0x68*128 ) 571 (self.Next, self.Depth, self.Seq, self.CtrZone, self.LowFragHeap,\ 572 self.Sequence1, self.Sequence2) = struct.unpack("LHHLLLL", mem[:0x18]) 573 index = 0x18 574 self.SegmentInfo = [] 575 for a in range(0, 128): 576 l = LocalSegmentInfo( imm, self.address + index,\ 577 mem[ index : index + 0x68] ) 578 index+= 0x68 579 self.SegmentInfo.append( l )
580 581 # What the real size of this, it is 0x64 or 0x68?
582 -class LocalSegmentInfo:
583 - def __init__(self, imm, addr, mem = ""):
584 self.address = addr 585 self.SubSegment = [] 586 self.imm = imm 587 if not mem: 588 mem = imm.readMemory( self.address, 0x68 ) 589 590 (self.Hint, self.ActiveSubsegment) = struct.unpack("LL", mem[0:8] ) 591 index = 8 592 self.CachedItems = struct.unpack("L" * 0x10, mem[ index : index + 0x10*4]) 593 index += 0x10*4 594 (self.Next, self.Depth, self.Seq, self.TotalBlocks,\ 595 self.SubSegmentCounts, self.LocalData, self.LastOpSequence,\ 596 self.BucketIndex, self.LastUsed, self.Reserved) = struct.unpack("LHHLLLLHHL", mem[index: index + 0x20]) 597 598 if self.Hint: 599 self.SubSegment.append( self.getSubSegment( self.Hint, "Hint" ) ) 600 if self.ActiveSubsegment and self.ActiveSubsegment != self.Hint: 601 self.SubSegment.append( self.getSubSegment( self.ActiveSubsegment, "ActiveSS") ) 602 for a in range( 0, len(self.CachedItems) ): 603 item = self.CachedItems[a] 604 if item and item not in (self.Hint, self.ActiveSubsegment): 605 self.SubSegment.append( self.getSubSegment( item, "Cache_%02x" % a) )
606 607 608
609 - def getSubSegment(self, address, type = ""):
610 return SubSegment(self.imm, address, type)
611
612 -class SubSegment:
613 - def __init__(self, imm, address, type=""):
614 self.address = address 615 self.type = type 616 self.chunks = [] 617 mem = imm.readMemory( address, 0x20 ) 618 (self.LocalInfo, self.UserBlocks, self.AggregateExchg,\ 619 self.Aggregate_Sequence, self.BlockSize, self.Flags,\ 620 self.BlockCount, self.SizeIndex, self.AffinityIndex, 621 self.Next, self.Lock) = struct.unpack("LLLLHHHBBLL", mem) 622 self.Offset = self.AggregateExchg >> 0xD 623 self.Offset = self.Offset & 0x7FFF8 624 self.Depth = self.AggregateExchg & 0xFFFF 625 #imm.log("UserBlock %s: 0x%08x size: %x offset: %x Depth: %x (0x%08x)" % ( self.type, self.UserBlocks, self.BlockSize, self.Offset, self.Depth, self.Next), address = self.UserBlocks) 626 if self.UserBlocks: 627 self.UserDataHeader = self.getUserData( imm, self.UserBlocks ) 628 629 # XXX: We need to check the "Next" for more chunks 630 list = self.grabBusyList( imm, self.UserBlocks, self.Offset, self.Depth) 631 self.chunks = self.getChunks( imm, self.UserBlocks + self.UserDataHeader.getSize(), list )
632
633 - def grabBusyList(self, imm, base_addr, offset, depth):
634 list = {} 635 i = 1 636 for a in range(0, depth): 637 address = base_addr + offset 638 dword = imm.readLong( address + 8 ) 639 offset = dword & 0xFFFF 640 offset *=8 641 list[ address ] = a + 1 642 return list
643
644 - def getUserData(self, imm, addr):
645 return UserData( imm, addr )
646
647 - def getChunks(self, imm, address, list):
648 #mem = imm.readMemory( self.UserBlocks, self.BlockSize * self.BlockCount) 649 addr = address 650 chunks = [] 651 for a in range(0, self.BlockCount): 652 c = win32vistaheapchunk(imm, addr, BlockSize = self.BlockSize) 653 s = "B" 654 if list.has_key(addr): 655 c.setFreeOrder( list[addr] ) 656 s = "F(%02d)" % list[addr] 657 if DEBUG: 658 imm.log("Chunk size: 0x%x lfhflag: 0x%x %s" % ( self.BlockSize, c.lfhflags, s ), address = addr) 659 addr += self.BlockSize*8 660 chunks.append( c ) 661 return chunks
662
663 -class UserData:
664 - def __init__(self, imm, addr):
665 self.address = addr 666 mem = imm.readMemory(addr, 0x10) 667 (self.SubSegment, self.Reserved, self.SizeIndex, self.Signature) =\ 668 struct.unpack("LLLL", mem)
669 - def getSize(self):
670 return 0x10
671
672 -class Bucket:
673 - def __init__(self, addr, mem):
674 self.address = addr 675 (self.BlockUnits, self.SizeIndex, Flag) =\ 676 struct.unpack("HBB", mem[:4]) 677 # Theoretically, this is how the Flag are separated: 678 self.UseAffinity = Flag & 0x1 679 self.DebugFlags = (Flag >1) & 0x3
680
681 -class HeapCache:
682 - def __init__(self, imm, addr):
683 self.addr = addr 684 self.fmt = "LLQQQLLQQLLLLLLLLLLQ" 685 size = struct.calcsize(self.fmt) 686 mem = imm.readMemory(addr, size) 687 imm.log(" 0x%08x - %d - %d" % (addr, size, len(mem)) ) 688 (self.NumBuckets, self.CommittedSize, self.CounterFrequency, self.AverageAllocTime, self.AverageFreeTime, 689 self.SampleCounter, self.field_24, self.AllocTimeRunningTotal, self.FreeTimeRunningTotal, self.AllocTimeCount, self.FreeTimeCount, 690 self.Depth, self.HighDepth, self.LowDepth, self.Sequence, self.ExtendCount, self.CreateUCRCount, self.LargestHighDepth, self.HighLowDifference, 691 self.pBitmap) = struct.unpack(self.fmt, mem) 692 693 addr += size 694 self.Buckets = struct.unpack("L"* self.NumBuckets, imm.readMemory(addr, self.NumBuckets*4)) 695 addr += self.NumBuckets*4 696 self.Bitmask = struct.unpack("L"* (self.NumBuckets/32/4), imm.readMemory(addr, self.NumBuckets/32)) 697 698 for a in range(0, len(self.Buckets)): 699 if self.Buckets[a]: 700 imm.log("HEAP_CACHE[%04x] = 0x%08x" % (a+0x80, self.Buckets[a]) )
701 702 703 #HEAP_FREE_ENTRY *Buckets[NumBuckets]; 704 705 #unsigned int Bitmap[NumBuckets/32]; 706 707 708
709 -class UserMemoryCache:
710 - def __init__(self, addr, mem):
711 self.address = addr 712 (self.Next, self.Depth, self.Sequence, self.AvailableBlocks,\ 713 self.Reserved) = struct.unpack("LHHLL", mem[ 0 : 16 ])
714
715 -class Blocks:
716 - def __init__(self, imm, addr):
717 mem = imm.readMemory( addr, 0x24 ) 718 if not mem: 719 raise Exception, "Can't read Block at 0x%08x" % addr 720 self.address = addr 721 self.FreeListInUse = None 722 self.FreeList = [] 723 (self.ExtendedLookup, self.ArraySize, self.ExtraItem, self.ItemCount, 724 self.OutOfRangeItems, self.BaseIndex, self.ListHead,\ 725 self.ListsInUseUlong, self.ListHints) =\ 726 struct.unpack( "L" * 9, mem )
727
728 - def setFreeListInUse(self, inuse):
729 self.FreeListInUse = inuse
730
731 - def setFreeList(self, flist):
732 self.FreeList = flist
733 734 SHOWCHUNK_FULL = 0x1 735 CHUNK_ANALIZE = 0x2
736 -class win32heapchunk:
737 FLAGS = { 'EXTRA PRESENT':('E', 0x2), 'FILL PATTERN':('FP', 0x4),\ 738 'VIRTUAL ALLOC': ('V', 0x8), 'TOP': ('T', 0x10), 739 'FFU1':('FFU1',0x20), 'FFU2': ('FFU2', 0x40),\ 740 'NO COALESCE':('NC', 0x80) } 741 BUSY = ('BUSY', ('B', 0x1))
742 - def __init__(self, imm, addr, heap = None):
743 """ Win32 Chunk """ 744 self.imm = imm # later replace it with heap.imm 745 746 self.restored = False 747 self.Lookaside = False 748 749 if heap: 750 self.heap_addr = heap.address 751 else: 752 self.heap_addr = 0 753 self.nextchunk=0 754 self.prevchunk=0 755 self.addr = addr 756 try: 757 dword1 = self.imm.readLong(addr) 758 dword2 = self.imm.readLong(addr+4) 759 except Exception: 760 raise Exception, "Failed to read chunk at address: 0x%08x" % addr 761 762 self._get( dword1, dword2, addr, heap)
763
764 - def _get(self, size, flags, addr, heap):
765 self.size = size & 0xffff 766 self.usize = self.size * 8 # unpacked 767 768 self.psize = ( size >> 16 ) & 0xffff 769 self.upsize = self.psize * 8 770 771 self.field4 = flags & 0xff 772 self.flags = (flags >> 8) & 0xff 773 self.other = (flags >> 16) & 0xffff 774 mem_addr = addr + 8 775 if not (self.flags & self.BUSY[1][1] ): 776 777 if self.flags & self.FLAGS['VIRTUAL ALLOC'][1]: 778 pass 779 else: 780 try: 781 self.nextchunk= self.imm.readLong(addr+8) 782 self.prevchunk= self.imm.readLong(addr+12) 783 except WindowsError: 784 raise Exception, "Failed to read chunk at address: 0x%08x" % addr 785 786 mem_addr +=8 787 else: 788 if self.size < 0x80: 789 if heap: 790 if heap.Lookaside: 791 if addr in heap.Lookaside[self.size].getList(): 792 self.Lookaside = True 793 794 self.data_addr = mem_addr 795 self.data_size = self.upsize - (addr - mem_addr) 796 797 try: 798 self.sample = self.imm.readMemory(self.data_addr, 0x10) 799 except WindowsError: 800 raise Exception, "Failed to read chunk at address: 0x%08x" % addr 801 802 self.properties= {'size': self.usize, 'prevsize': self.upsize, 'field4': self.field4,\ 803 'flags':self.flags, 'other':self.other, 'address':self.addr,\ 804 'next': self.nextchunk, 'prev': self.prevchunk}
805
806 - def setRestored(self):
807 self.restored = True
808
809 - def isRestore(self):
810 return self.restored
811
812 - def get(self, what):
813 try: 814 return self.properties[string.lower(what)] 815 except KeyError: 816 return None
817
818 - def printchunk(self, uselog= None, option=0, dt= None):
819 ret = [] 820 if self.isRestore(): 821 restore = "<R>" 822 else: 823 restore = "" 824 ret.append((self.addr, "0x%08x> " % self.addr + "size: 0x%08x (%04x) prevsize: 0x%08x (%04x) %s" % (self.usize, self.size, \ 825 self.upsize, self.psize, restore) )) 826 ret.append((self.addr, " heap: *0x%08x* flags: 0x%08x (%s)" % (self.heap_addr, self.flags,\ 827 self.getflags(self.flags)))) 828 #print "unused: 0x%08x flags: 0x%08x (%s)" % (self.field4, self.flags,\ 829 # self.getflags(self.flags)) 830 if not (self.flags & self.BUSY[1][1]): 831 ret.append((self.addr, " next: 0x%08x prev: 0x%08x" % (self.nextchunk, self.prevchunk))) 832 if option & SHOWCHUNK_FULL: 833 dump = immutils.hexdump(self.sample) 834 for a in range(0, len(dump)): 835 if not a: 836 ret.append((self.addr, " (%s %s)" % (dump[a][0], dump[a][1]))) 837 if dt: 838 result = dt.Discover(self.imm.readMemory(self.data_addr, self.data_size), self.data_addr) 839 #self.imm.log( str(ret )) 840 for obj in result: 841 msg = obj.Print() 842 ret.append((obj.address, " > %s: %s " % (obj.name, msg) )) 843 #imm.log( "obj: %s: %s %d" % (obj.name, msg, obj.getSize() ), address = obj.address) 844 845 if uselog: 846 for adr, msg in ret: 847 uselog(msg, address = adr) 848 849 return ret
850
851 - def getflags(self, flag):
852 f="" 853 if self.flags & self.BUSY[1][1]: 854 f+=self.BUSY[1][0] 855 else: 856 f+="F" 857 858 for a in self.FLAGS.keys(): 859 if self.FLAGS[a][1] & self.flags: 860 f+="|" + self.FLAGS[a][0] 861 if self.Lookaside: 862 f += "$" 863 864 return f
865
866 - def istop(self):
867 if self.flags & self.FLAGS['TOP'][1]: 868 return 1 869 return 0
870
871 - def isfirst(self):
872 if self.psize == 0: 873 return 1 874 return 0
875 876
877 -class win32vistaheapchunk(win32heapchunk):
878 FLAGS = { 'FILL PATTERN':('FP', 0x4), 'DEBUG': ('D', 0x8),\ 879 'TOP': ('T', 0x10), 'FFU1':('FFU1',0x20),\ 880 'FFU2': ('FFU2', 0x40), 'NO COALESCE':('NC', 0x80) } 881 LFHMASK = 0x3F 882 LFHFLAGS = { 'TOP': ('T', 0x3), 'BUSY': ('B', 0x18) } 883
884 - def __init__(self, imm, addr, heap = None, BlockSize = 0):
885 self.heap = heap 886 self.freeorder = -1 887 self.isLFH = False 888 if BlockSize: 889 self.isLFH = True 890 self.size = BlockSize 891 win32heapchunk.__init__(self, imm, addr, heap)
892
893 - def setFreeOrder(self, freeorder):
894 self.freeorder = freeorder
895
896 - def _get(self, dword1, dword2, addr, heap):
897 heap = self.heap 898 self.nextchunk= 0 899 self.prevchunk= 0 900 if heap and heap.EncodeFlagMask: 901 dword1 ^= heap.EncodingKey 902 dword2 = dword2 ^ heap.EncodingKey2 903 904 self.subsegmentcode = self.SubSegmentCode = dword1 905 if self.isLFH: 906 self.upsize = self.usize = self.size << 3 907 self.psize = self.size 908 else: 909 self.size = dword1 & 0xffff 910 self.usize = self.size << 3 911 self.psize = dword2 & 0xffff 912 self.upsize = self.psize << 3 913 914 self.flags = (dword1 >> 16 & 0xff) 915 self.smalltagindex = (dword1 >> 24 & 0xff) 916 917 self.segmentoffset = (dword2 >> 16 & 0xff) 918 self.unused = (dword2 >> 24 & 0xff) 919 self.flags2 = self.unused # LOW FRAGMENTATION HEAP FLAGS 920 self.lfhflags = self.flags2 921 922 if not (self.flags & 0x1): 923 try: 924 (self.nextchunk, self.prevchunk) = struct.unpack("LL", self.imm.readMemory( addr+8, 8) ) 925 except: 926 pass 927 928 929 self.data_addr = addr + 8 930 931 self.properties= {'size': self.usize, 'prevsize': self.upsize, 'smalltagindex': self.smalltagindex,\ 932 'flags':self.flags, 'subsegmentcode':self.subsegmentcode, 'address':self.addr,\ 933 'next': self.nextchunk, 'prev': self.prevchunk, 'lfhflags': self.flags2,\ 934 'segmentoffset': self.segmentoffset } 935 self.data_size = self.usize - (self.addr - self.data_addr) 936 if DEBUG: 937 self.imm.log("Datasize: 0x%08x 0x%d" % (self.data_addr, self.data_size), address = self.addr) 938 try: 939 self.sample = self.imm.readMemory(self.data_addr, 0x10) 940 except WindowsError: 941 raise Exception, "Failed to read chunk at address: 0x%08x" % addr
942
943 - def getflags(self, flag):
944 f="" 945 if not self.isLFH: 946 if self.flags & self.BUSY[1][1]: 947 f+=self.BUSY[1][0] 948 else: 949 f+="F" 950 951 for a in self.FLAGS.keys(): 952 if self.FLAGS[a][1] & self.flags: 953 f+="|" + self.FLAGS[a][0] 954 else: 955 for k in self.LFHFLAGS.keys(): 956 if self.flags2 == self.LFHFLAGS[k][1]: 957 return self.LFHFLAGS[k][0] 958 return f
959
960 - def istop(self):
961 if self.flags2 == self.LFHFLAGS['TOP'][1] : 962 return 1 963 else: 964 return 0
965
966 - def printchunk(self, uselog= None, option=0, dt= None):
967 ret = [] 968 if self.isRestore(): 969 restore = "<R>" 970 else: 971 restore = "" 972 if self.isLFH: 973 s = "B" 974 if self.freeorder != -1: 975 s="F(%02x)" % self.freeorder 976 ret.append( (self.addr, "Chunk size: 0x%x lfhflag: 0x%x %s" % ( self.psize, self.lfhflags, s )) ) 977 else: 978 ret.append((self.addr, "0x%08x> " % self.addr + "size: 0x%08x (%04x) prevsize: 0x%08x (%04x) %s" % (self.usize, self.size, \ 979 self.upsize, self.psize, restore) )) 980 ret.append((self.addr, " heap: *0x%08x* flags: 0x%02x 0x%02x (%s)" % (self.heap_addr, self.flags, self.flags2,\ 981 self.getflags(self.flags)))) 982 if not self.isLFH and not (self.flags2 & self.BUSY[1][1]): 983 ret.append((self.addr, " next: 0x%08x prev: 0x%08x" % (self.nextchunk, self.prevchunk))) 984 if option & SHOWCHUNK_FULL: 985 dump = immutils.hexdump(self.sample) 986 for a in range(0, len(dump)): 987 if not a: 988 ret.append((self.addr, " (%s %s)" % (dump[a][0], dump[a][1]))) 989 if dt: 990 if not self.isLFH or (self.isLFH and self.freeorder == -1) : 991 result = dt.Discover(self.imm.readMemory(self.data_addr, self.data_size), self.data_addr) 992 for obj in result: 993 msg = obj.Print() 994 ret.append((obj.address, " > %s: %s " % (obj.name, msg) )) 995 996 if uselog: 997 for adr, msg in ret: 998 uselog(msg, address = adr) 999 1000 return ret
1001 1002
1003 -class PHeapLookaside(UserList):
1004 - def __init__(self, imm, addr, heap = 0x0, log = None ):
1005 """ Win32 Heap Lookaside list """ 1006 UserList.__init__(self) 1007 if not log: 1008 log = imm.log 1009 self.log = log 1010 self.imm = imm 1011 self.heap = heap 1012 self.Lookaside = [] 1013 1014 LookSize = PLook(self.imm, 0x0).getSize() 1015 mem = imm.readMemory(addr, LookSize * HEAP_MAX_FREELIST) 1016 1017 for ndx in range(0, HEAP_MAX_FREELIST): 1018 base_addr = addr + ndx * LookSize 1019 l = PLook(self.imm, base_addr, mem[ ndx * LookSize : ndx * LookSize + LookSize ], self.heap ) 1020 1021 self.data.append(l) 1022 next = l.ListHead 1023 while next and next != base_addr: 1024 l.append( next - 8 ) 1025 try: 1026 next = self.imm.readLong(next) 1027 except: 1028 break
1029 1030
1031 -class PLook:
1032 - def __init__(self, imm, addr, data = None, heap = 0x0, log= None):
1033 self.log = log 1034 self.addr = addr 1035 self.List = [] 1036 self.fmt = "LHHLLLLLLL12s" 1037 self.imm = imm 1038 self.heap = heap 1039 1040 # XXX: This need some check, cause my calculation might be wrong 1041 if data: 1042 (self.ListHead, self.Depth, self.MaxDepth, none, self.TotalAlloc, self.AllocMiss, self.TotalFrees, 1043 self.FreeMiss, self.AllocLastTotal, self.LastAllocateMiss, self.Unknown) = \ 1044 struct.unpack(self.fmt, data[:struct.calcsize(self.fmt)]) 1045 elif addr: 1046 data = self.imm.readMemory(addr, self.getSize() ) 1047 (self.ListHead, self.Depth, self.MaxDepth, none, self.TotalAlloc, self.AllocMiss, self.TotalFrees, 1048 self.FreeMiss, self.AllocLastTotal, self.LastAllocateMiss, self.Unknown1, self.Unknown2) = \ 1049 struct.unpack(self.fmt, data[:struct.calcsize(self.fmt)])
1050
1051 - def isEmpty(self):
1052 return self.ListHead == 0x0
1053
1054 - def getSize(self):
1055 return struct.calcsize(self.fmt)
1056
1057 - def append(self, andres):
1058 self.List.append(andres)
1059
1060 - def getList(self):
1061 """get a the single linked list of the Lookaside entry 1062 @return: A list of the address of the linked list""" 1063 return self.List
1064
1065 - def getChunks(self):
1066 """get a the single linked list of the Lookaside entry 1067 @return: A list of the Chunks on the linked list""" 1068 1069 chunks = [] 1070 for addr in self.List: 1071 # The Address of the Single Linked list of the Lookaside points to the data of the chunk. 1072 # so, we need to increase 8 bytes to get into the begging of the header 1073 chunks.append( win32heapchunk(self.imm, addr - 8, self.heap ) ) 1074 1075 return chunks
1076
1077 -class SearchHeap:
1078 - def __init__(self, imm, what, action, value, heap = 0x0, restore = False, option = 0):
1079 """ 1080 Search the Heap for specific Chunks 1081 1082 @type imm: Debugger Object 1083 @param imm: Initialized debugged object 1084 1085 @type what: STRING 1086 @param what: Chunk property to search from (size, prevsize, field4, flags, other, address, next, prev) 1087 1088 @type action: STRING 1089 @param action: Type of search ( =, >, <, >=, <=, &, not, !=) 1090 1091 @type value: DWORD 1092 @param value: Value to search for 1093 1094 @type heap: DWORD 1095 @param heap: (Optional, Def=None) Filter by Heap 1096 1097 @type restore: BOOLEAN 1098 @param restore: (Optional, Def: False) Flag whether or not use a restore heap (Useful if you want to search on a broken heap) 1099 1100 @type option: DWORD 1101 @param option: (Optional, Def: None) Chunk's display option 1102 """ 1103 self.functions = { '=': lambda a, b: a==b, 1104 '>': lambda a,b : a>b, 1105 '<': lambda a,b : a<b, 1106 '>=': lambda a,b : a>=b, 1107 '<=': lambda a,b : a<=b, 1108 '&': lambda a,b : a&b, 1109 'not': lambda a,b: a & ~b, 1110 #'find': lambda a,b: a.find(b) > -1, 1111 '!=': lambda a,b : a!=b 1112 } 1113 for a in imm.getHeapsAddress(): 1114 if a==heap or not heap: 1115 #imm.log("Dumping heap: 0x%08x" % a, address = a, focus = 1 ) 1116 p = imm.getHeap( a, restore ) 1117 if not what or not action: 1118 for c in p.chunks: 1119 c.printchunk(uselog = imm.log, option = option) 1120 else: 1121 for c in p.chunks: 1122 if self.functions[action](c.get(what) , value): 1123 c.printchunk(uselog = imm.log, option = option)
1124