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