Kali下使用libheap

Kali下使用libheap

    在github上,可以libheap用来帮助调试堆溢出。链接见:https://github.com/cloudburst/libheap 但是最后一次更新在一年前了,我直接拿着在Kali 2.0上使用时,会出错,比如:

    我就对其进行了修改,保证可以在Kali 2.0下完美使用,32位和64位都可以使用。我的系统版本:

    以后碰见其它Linux发行版,都可以对其修改。在不同的发行版之间,在malloc_state和malloc_par结构体中,稍微有些区别,只需要下载相应的glibc源码,根据源码对libheap进行修改就可以了。在Kali 64位下,int是4字节,long是8字节,size_t是8字节。修改后的libheap见:

   1 from __future__ import print_function
   2 #modify by wah for kali 2.0
   3 try:
   4     import gdb
   5 except ImportError:
   6     print("Not running inside of GDB, exiting...")
   7     exit()
   8 
   9 import sys
  10 import struct
  11 from os import uname
  12 
  13 # bash color support
  14 color_support = True
  15 if color_support:
  16     c_red      = "33[31m"
  17     c_red_b    = "33[01;31m"
  18     c_green    = "33[32m"
  19     c_green_b  = "33[01;32m"
  20     c_yellow   = "33[33m"
  21     c_yellow_b = "33[01;33m"
  22     c_blue     = "33[34m"
  23     c_blue_b   = "33[01;34m"
  24     c_purple   = "33[35m"
  25     c_purple_b = "33[01;35m"
  26     c_teal     = "33[36m"
  27     c_teal_b   = "33[01;36m"
  28     c_none     = "33[0m"
  29 else:
  30     c_red      = ""
  31     c_red_b    = ""
  32     c_green    = ""
  33     c_green_b  = ""
  34     c_yellow   = ""
  35     c_yellow_b = ""
  36     c_blue     = ""
  37     c_blue_b   = ""
  38     c_purple   = ""
  39     c_purple_b = ""
  40     c_teal     = ""
  41     c_teal_b   = ""
  42     c_none     = ""
  43 c_error  = c_red
  44 c_title  = c_green_b
  45 c_header = c_yellow_b
  46 c_value  = c_blue_b
  47 
  48 ################################################################################
  49 # MALLOC CONSTANTS AND MACROS
  50 ################################################################################
  51 
  52 _machine = uname()[4]
  53 if _machine == "x86_64":
  54     SIZE_SZ = 8
  55 elif _machine in ("i386", "i686"):
  56     SIZE_SZ = 4
  57 
  58 MIN_CHUNK_SIZE    = 4 * SIZE_SZ
  59 MALLOC_ALIGNMENT  = 2 * SIZE_SZ
  60 MALLOC_ALIGN_MASK = MALLOC_ALIGNMENT - 1
  61 MINSIZE           = (MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK
  62 
  63 def chunk2mem(p):
  64     "conversion from malloc header to user pointer"
  65     return (p.address + (2*SIZE_SZ))
  66 
  67 def mem2chunk(mem):
  68     "conversion from user pointer to malloc header"
  69     return (mem - (2*SIZE_SZ))
  70 
  71 def request2size(req):
  72     "pad request bytes into a usable size"
  73 
  74     if (req + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE):
  75         return MINSIZE
  76     else:
  77         return (int(req + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
  78 
  79 PREV_INUSE     = 1
  80 IS_MMAPPED     = 2
  81 NON_MAIN_ARENA = 4
  82 SIZE_BITS      = (PREV_INUSE|IS_MMAPPED|NON_MAIN_ARENA)
  83 
  84 def prev_inuse(p):
  85     "extract inuse bit of previous chunk"
  86     return (p.size & PREV_INUSE)
  87 
  88 def chunk_is_mmapped(p):
  89     "check for mmap()'ed chunk"
  90     return (p.size & IS_MMAPPED)
  91 
  92 def chunk_non_main_arena(p):
  93     "check for chunk from non-main arena"
  94     return (p.size & NON_MAIN_ARENA)
  95 
  96 def chunksize(p):
  97     "Get size, ignoring use bits"
  98     return (p.size & ~SIZE_BITS)
  99 
 100 def next_chunk(p):
 101     "Ptr to next physical malloc_chunk."
 102     return (p.address + (p.size & ~SIZE_BITS))
 103 
 104 def prev_chunk(p):
 105     "Ptr to previous physical malloc_chunk"
 106     return (p.address - p.prev_size)
 107 
 108 def chunk_at_offset(p, s):
 109     "Treat space at ptr + offset as a chunk"
 110     return malloc_chunk(p.address + s, inuse=False)
 111 
 112 def inuse(p):
 113     "extract p's inuse bit"
 114     return (malloc_chunk(p.address + 
 115             (p.size & ~SIZE_BITS), inuse=False).size & PREV_INUSE)
 116 
 117 def set_inuse(p):
 118     "set chunk as being inuse without otherwise disturbing"
 119     chunk = malloc_chunk((p.address + (p.size & ~SIZE_BITS)), inuse=False)
 120     chunk.size |= PREV_INUSE
 121     chunk.write()
 122 
 123 def clear_inuse(p):
 124     "clear chunk as being inuse without otherwise disturbing"
 125     chunk = malloc_chunk((p.address + (p.size & ~SIZE_BITS)), inuse=False)
 126     chunk.size &= ~PREV_INUSE
 127     chunk.write()
 128 
 129 def inuse_bit_at_offset(p, s):
 130     "check inuse bits in known places"
 131     return (malloc_chunk((p.address + s), inuse=False).size & PREV_INUSE)
 132 
 133 def set_inuse_bit_at_offset(p, s):
 134     "set inuse bits in known places"
 135     chunk = malloc_chunk((p.address + s), inuse=False)
 136     chunk.size |= PREV_INUSE
 137     chunk.write()
 138 
 139 def clear_inuse_bit_at_offset(p, s):
 140     "clear inuse bits in known places"
 141     chunk = malloc_chunk((p.address + s), inuse=False)
 142     chunk.size &= ~PREV_INUSE
 143     chunk.write()
 144 
 145 def bin_at(m, i):
 146     "addressing -- note that bin_at(0) does not exist"
 147     if SIZE_SZ == 4:
 148         offsetof_fd = 0x8
 149         return (gdb.parse_and_eval("&main_arena.bins[%d]" % 
 150             ((i -1) * 2)).cast(gdb.lookup_type('unsigned int')) - offsetof_fd)
 151     elif SIZE_SZ == 8:
 152         offsetof_fd = 0x10
 153         return (gdb.parse_and_eval("&main_arena.bins[%d]" % 
 154             ((i -1) * 2)).cast(gdb.lookup_type('unsigned long')) - offsetof_fd)
 155 
 156 def next_bin(b):
 157     return (b + 1)
 158 
 159 def first(b):
 160     return b.fd
 161 
 162 def last(b):
 163     return b.bk
 164 
 165 NBINS          = 128
 166 NSMALLBINS     = 64
 167 SMALLBIN_WIDTH = MALLOC_ALIGNMENT
 168 MIN_LARGE_SIZE = (NSMALLBINS * SMALLBIN_WIDTH)
 169 
 170 def in_smallbin_range(sz):
 171     "check if size is in smallbin range"
 172     return (sz < MIN_LARGE_SIZE)
 173 
 174 def smallbin_index(sz):
 175     "return the smallbin index"
 176 
 177     if SMALLBIN_WIDTH == 16:
 178         return (sz >> 4)
 179     else:
 180         return (sz >> 3)
 181 
 182 def largebin_index_32(sz):
 183     "return the 32bit largebin index"
 184 
 185     if (sz >> 6) <= 38:
 186         return (56 + (sz >> 6))
 187     elif (sz >> 9) <= 20:
 188         return (91 + (sz >> 9))
 189     elif (sz >> 12) <= 10:
 190         return (110 + (sz >> 12))
 191     elif (sz >> 15) <= 4:
 192         return (119 + (sz >> 15))
 193     elif (sz >> 18) <= 2:
 194         return (124 + (sz >> 18))
 195     else:
 196         return 126
 197 
 198 def largebin_index_64(sz):
 199     "return the 64bit largebin index"
 200 
 201     if (sz >> 6) <= 48:
 202         return (48 + (sz >> 6))
 203     elif (sz >> 9) <= 20:
 204         return (91 + (sz >> 9))
 205     elif (sz >> 12) <= 10:
 206         return (110 + (sz >> 12))
 207     elif (sz >> 15) <= 4:
 208         return (119 + (sz >> 15))
 209     elif (sz >> 18) <= 2:
 210         return (124 + (sz >> 18))
 211     else:
 212         return 126
 213 
 214 def largebin_index(sz):
 215     "return the largebin index"
 216 
 217     if SIZE_SZ == 8:
 218         return largebin_index_64(sz)
 219     else:
 220         return largebin_index_32(sz)
 221 
 222 def bin_index(sz):
 223     "return the bin index"
 224 
 225     if in_smallbin_range(sz):
 226         return smallbin_index(sz)
 227     else:
 228         return largebin_index(sz)
 229 
 230 BINMAPSHIFT = 5
 231 BITSPERMAP  = 1 << BINMAPSHIFT
 232 BINMAPSIZE  = (NBINS / BITSPERMAP)
 233 
 234 def fastbin(ar_ptr, idx):
 235     return ar_ptr.fastbinsY[idx]
 236 
 237 def fastbin_index(sz):
 238     "offset 2 to use otherwise unindexable first 2 bins"
 239     if SIZE_SZ == 8:
 240         return ((sz >> 4) - 2)
 241     else:
 242         return ((sz >> 3) - 2)
 243 
 244 MAX_FAST_SIZE = (80 * SIZE_SZ / 4)
 245 NFASTBINS     = (fastbin_index(request2size(MAX_FAST_SIZE)) + 1)
 246 
 247 FASTCHUNKS_BIT = 0x1
 248 
 249 def have_fastchunks(M):
 250     return ((M.flags & FASTCHUNKS_BIT) == 0)
 251 
 252 def clear_fastchunks(M, inferior=None):
 253     if inferior == None:
 254         inferior = get_inferior()
 255 
 256     M.flags |= FASTCHUNKS_BIT
 257     inferior.write_memory(M.address, struct.pack("<I", M.flags))
 258 
 259 def set_fastchunks(M, inferior=None):
 260     if inferior == None:
 261         inferior = get_inferior()
 262 
 263     M.flags &= ~FASTCHUNKS_BIT
 264     inferior.write_memory(M.address, struct.pack("<I", M.flags))
 265 
 266 NONCONTIGUOUS_BIT = 0x2
 267 
 268 def contiguous(M):
 269     return ((M.flags & NONCONTIGUOUS_BIT) == 0)
 270 
 271 def noncontiguous(M):
 272     return ((M.flags & NONCONTIGUOUS_BIT) != 0)
 273 
 274 def set_noncontiguous(M, inferior=None):
 275     if inferior == None:
 276         inferior = get_inferior()
 277 
 278     M.flags |= NONCONTIGUOUS_BIT
 279     inferior.write_memory(M.address, struct.pack("<I", M.flags))
 280 
 281 def set_contiguous(M, inferior=None):
 282     if inferior == None:
 283         inferior = get_inferior()
 284 
 285     M.flags &= ~NONCONTIGUOUS_BIT
 286     inferior.write_memory(M.address, struct.pack("<I", M.flags))
 287 
 288 def get_max_fast():
 289     return gdb.parse_and_eval("global_max_fast")
 290 
 291 def mutex_lock(ar_ptr, inferior=None):
 292     if inferior == None:
 293         inferior = get_inferior()
 294 
 295     ar_ptr.mutex = 1
 296     inferior.write_memory(ar_ptr.address, struct.pack("<I", ar_ptr.mutex))
 297 
 298 def mutex_unlock(ar_ptr, inferior=None):
 299     if inferior == None:
 300         inferior = get_inferior()
 301 
 302     ar_ptr.mutex = 0
 303     inferior.write_memory(ar_ptr.address, struct.pack("<I", ar_ptr.mutex))
 304 
 305 def get_inferior():
 306     try:
 307         if len(gdb.inferiors()) == 0:
 308             print(c_error + "No gdb inferior could be found." + c_none)
 309             return -1
 310         else:
 311             inferior = gdb.inferiors()[0]
 312             return inferior
 313     except AttributeError:
 314         print(c_error + "This gdb's python support is too old." + c_none)
 315         exit()
 316 
 317 
 318 ################################################################################
 319 class malloc_chunk:
 320     "python representation of a struct malloc_chunk"
 321 
 322     def __init__(self,addr=None,mem=None,size=None,inferior=None,inuse=False,read_data=True):
 323         self.prev_size   = 0
 324         self.size        = 0
 325         self.data        = None
 326         self.fd          = None
 327         self.bk          = None
 328         self.fd_nextsize = None
 329         self.bk_nextsize = None
 330 
 331         if addr == None or addr == 0:
 332             if mem == None:
 333                 sys.stdout.write(c_error)
 334                 print("Please specify a valid struct malloc_chunk address.", end=' ')
 335                 sys.stdout.write(c_none)
 336                 return None
 337 
 338             self.address = None
 339         else:
 340             self.address = addr
 341 
 342         if inferior == None and mem == None:
 343             inferior = get_inferior()
 344             if inferior == -1:
 345                 return None
 346 
 347         if mem == None:
 348             # a string of raw memory was not provided
 349             try:
 350                 if SIZE_SZ == 4:
 351                     mem = inferior.read_memory(addr, 0x8)
 352                 elif SIZE_SZ == 8:
 353                     mem = inferior.read_memory(addr, 0x10)
 354             except TypeError:
 355                 print(c_error + "Invalid address specified." + c_none)
 356                 return None
 357             except RuntimeError:
 358                 print(c_error + "Could not read address 0x%x" % addr + c_none)
 359                 return None
 360         else:
 361             # a string of raw memory was provided
 362             if inuse:
 363                 if (len(mem)!=0x8) and (len(mem)<0x10):
 364                     sys.stdout.write(c_error)
 365                     print("Insufficient memory provided for a malloc_chunk.", end=' ')
 366                     sys.stdout.write(c_none)
 367                     return None
 368                 if len(mem)==0x8 or len(mem)==0x10:
 369                     #header only provided
 370                     read_data = False
 371             else:
 372                 if (len(mem)!=0x18) and (len(mem)<0x30):
 373                     sys.stdout.write(c_error)
 374                     print("Insufficient memory provided for a free chunk.", end=' ')
 375                     sys.stdout.write(c_none)
 376                     return None
 377 
 378         if SIZE_SZ == 4:
 379             (self.prev_size,
 380             self.size) = struct.unpack_from("<II", mem, 0x0)
 381         elif SIZE_SZ == 8:
 382             (self.prev_size,
 383             self.size) = struct.unpack_from("<QQ", mem, 0x0)
 384 
 385         if size == None:
 386             real_size = (self.size & ~SIZE_BITS)
 387         else:
 388             #a size was provided (for a malformed chunk with an invalid size)
 389             real_size = size & ~SIZE_BITS
 390 
 391         if inuse:
 392             if read_data:
 393                 if self.address != None:
 394                     # a string of raw memory was not provided
 395                     try:
 396                         mem = inferior.read_memory(addr, real_size + SIZE_SZ)
 397                     except TypeError:
 398                         print(c_error + "Invalid address specified." + c_none)
 399                         return None
 400                     except RuntimeError:
 401                         print(c_error + "Could not read address 0x%x" % addr 
 402                                 + c_none)
 403                         return None
 404 
 405                 real_size = (real_size - SIZE_SZ) / SIZE_SZ
 406                 if SIZE_SZ == 4:
 407                     self.data = struct.unpack_from("<%dI" % real_size, mem, 0x8)
 408                 elif SIZE_SZ == 8:
 409                     self.data = struct.unpack_from("<%dQ" %real_size, mem, 0x10)
 410 
 411         if not inuse:
 412             if self.address != None:
 413                 # a string of raw memory was not provided
 414                 if inferior != None:
 415                     if SIZE_SZ == 4:
 416                         mem = inferior.read_memory(addr, 0x18)
 417                     elif SIZE_SZ == 8:
 418                         mem = inferior.read_memory(addr, 0x30)
 419 
 420             if SIZE_SZ == 4:
 421                 (self.fd,         
 422                 self.bk,          
 423                 self.fd_nextsize, 
 424                 self.bk_nextsize) = struct.unpack_from("<IIII", mem, 0x8)
 425             elif SIZE_SZ == 8:
 426                 (self.fd,         
 427                 self.bk,          
 428                 self.fd_nextsize, 
 429                 self.bk_nextsize) = struct.unpack_from("<QQQQ", mem, 0x10)
 430 
 431     def write(self, inferior=None):
 432         if self.fd == None and self.bk == None:
 433             inuse = True
 434         else:
 435             inuse = False
 436 
 437         if inferior == None:
 438             inferior = get_inferior()
 439             if inferior == -1:
 440                 return None
 441 
 442         if inuse:
 443             if SIZE_SZ == 4:
 444                 mem = struct.pack("<II", self.prev_size, self.size)
 445                 if self.data != None:
 446                     mem += struct.pack("<%dI" % len(self.data), *self.data)
 447             elif SIZE_SZ == 8:
 448                 mem = struct.pack("<QQ", self.prev_size, self.size)
 449                 if self.data != None:
 450                     mem += struct.pack("<%dQ" % len(self.data), *self.data)
 451         else:
 452             if SIZE_SZ == 4:
 453                 mem = struct.pack("<IIIIII", self.prev_size, self.size, 
 454                         self.fd, self.bk, self.fd_nextsize, self.bk_nextsize)
 455             elif SIZE_SZ == 8:
 456                 mem = struct.pack("<QQQQQQ", self.prev_size, self.size, 
 457                         self.fd, self.bk, self.fd_nextsize, self.bk_nextsize)
 458 
 459         inferior.write_memory(self.address, mem)
 460 
 461     def __str__(self):
 462         if self.prev_size == 0 and self.size == 0:
 463             return ""
 464         elif self.fd == None and self.bk == None:
 465             ret =  "%s%s%x%s%x%s" %                               
 466                     (c_title + "struct malloc_chunk {",           
 467                     c_none + "
prev_size   = " + c_value + "0x", 
 468                     self.prev_size,                               
 469                     c_none + "
size        = " + c_value + "0x", 
 470                     self.size, c_none)
 471 
 472             if self.data != None:
 473                 if SIZE_SZ == 4:
 474                     ret += "%s%s%r" %                                       
 475                             ("
data        = " + c_value + str(self.data), 
 476                             c_none + "
raw         = " + c_value,          
 477                             struct.pack("<%dI"%len(self.data), *self.data))
 478                 elif SIZE_SZ == 8:
 479                     ret += "%s%s%r" %                                       
 480                             ("
data        = " + c_value + str(self.data), 
 481                             c_none + "
raw         = " + c_value,          
 482                             struct.pack("<%dQ"%len(self.data), *self.data))
 483                 ret += c_none
 484 
 485             return ret
 486         else:
 487             return "%s%s%x%s%x%s%lx%s%lx%s%lx%s%lx%s" %           
 488                     (c_title + "struct malloc_chunk {",           
 489                     c_none + "
prev_size   = " + c_value + "0x", 
 490                     self.prev_size,                               
 491                     c_none + "
size        = " + c_value + "0x", 
 492                     self.size,                                    
 493                     c_none + "
fd          = " + c_value + "0x", 
 494                     self.fd,                                      
 495                     c_none + "
bk          = " + c_value + "0x", 
 496                     self.bk,                                      
 497                     c_none + "
fd_nextsize = " + c_value + "0x", 
 498                     self.fd_nextsize,                             
 499                     c_none + "
bk_nextsize = " + c_value + "0x", 
 500                     self.bk_nextsize, c_none)
 501 
 502 ################################################################################
 503 class malloc_state:
 504     "python representation of a struct malloc_state"
 505 
 506     def __init__(self, addr=None, mem=None, inferior=None):
 507         self.mutex          = 0
 508         self.flags          = 0
 509         self.fastbinsY      = 0
 510         self.top            = 0
 511         self.last_remainder = 0
 512         self.bins           = 0
 513         self.binmap         = 0
 514         self.next           = 0
 515     self.next_free      = 0
 516         self.system_mem     = 0
 517         self.max_system_mem = 0
 518 
 519         if addr == None:
 520             if mem == None:
 521                 sys.stdout.write(c_error)
 522                 print("Please specify a struct malloc_state address.")
 523                 sys.stdout.write(c_none)
 524                 return None
 525 
 526             self.address = None
 527         else:
 528             self.address = addr
 529 
 530         if inferior == None and mem == None:
 531             inferior = get_inferior()
 532             if inferior == -1:
 533                 return None
 534 
 535         if mem == None:
 536             # a string of raw memory was not provided
 537             try:
 538                 if SIZE_SZ == 4:
 539                     mem = inferior.read_memory(addr, 0x450)
 540                 elif SIZE_SZ == 8:
 541                     mem = inferior.read_memory(addr, 0x888)
 542             except TypeError:
 543                 print(c_error + "Invalid address specified." + c_none)
 544                 return None
 545             except RuntimeError:
 546                 print(c_error + "Could not read address 0x%x" % addr + c_none)
 547                 return None
 548 
 549         if SIZE_SZ == 4:
 550             (self.mutex,         
 551             self.flags)          = struct.unpack_from("<II", mem, 0x0)
 552             self.fastbinsY       = struct.unpack_from("<10I", mem, 0x8)
 553             (self.top,           
 554             self.last_remainder) = struct.unpack_from("<II", mem, 0x30)
 555 
 556             self.bins            = struct.unpack_from("<254I", mem, 0x38)
 557             self.binmap          = struct.unpack_from("<IIII", mem, 0x430)
 558             (self.next,          
 559         self.next_free,      
 560             self.system_mem,     
 561             self.max_system_mem) = struct.unpack_from("<IIII", mem, 0x440)
 562         elif SIZE_SZ == 8:
 563             (self.mutex,         
 564             self.flags)          = struct.unpack_from("<II", mem, 0x0)
 565             self.fastbinsY       = struct.unpack_from("<10Q", mem, 0x8)
 566             (self.top,           
 567             self.last_remainder) = struct.unpack_from("<QQ", mem, 0x58)
 568             self.bins            = struct.unpack_from("<254Q", mem, 0x68)
 569             self.binmap          = struct.unpack_from("<IIII", mem, 0x858)
 570             (self.next,          
 571         self.next_free,      
 572             self.system_mem,     
 573             self.max_system_mem) = struct.unpack_from("<QQQQ", mem, 0x868)
 574 
 575     def write(self, inferior=None):
 576         if inferior == None:
 577             inferior = get_inferior()
 578             if inferior == -1:
 579                 return None
 580 
 581         if SIZE_SZ == 4:
 582             mem = struct.pack("<275I", self.mutex, self.flags, self.fastbinsY, 
 583                     self.top, self.last_remainder, self.bins, self.binmap, 
 584                     self.next, self.system_mem, self.max_system_mem)
 585         elif SIZE_SZ == 8:
 586             mem = struct.pack("<II266QIIIIQQQ", self.mutex, self.flags, 
 587                     self.fastbinsY, self.top, self.last_remainder, self.bins, 
 588                     self.binmap, self.next, self.system_mem, 
 589                     self.max_system_mem)
 590 
 591         inferior.write_memory(self.address, mem)
 592 
 593     def __str__(self):
 594         return "%s%s%x%s%x%s%s%lx%s%lx%s%s%s%lx%s%lx%s%lx%s" %      
 595                 (c_title + "struct malloc_state {",                 
 596                 c_none + "
mutex          = " + c_value + "0x",    
 597                 self.mutex,                                         
 598                 c_none + "
flags          = " + c_value + "0x",    
 599                 self.flags,                                         
 600                 c_none + "
fastbinsY      = " + c_value + "{...}", 
 601                 c_none + "
top            = " + c_value + "0x",    
 602                 self.top,                                           
 603                 c_none + "
last_remainder = " + c_value + "0x",    
 604                 self.last_remainder,                                
 605                 c_none + "
bins           = " + c_value + "{...}", 
 606                 c_none + "
binmap         = " + c_value + "{...}", 
 607                 c_none + "
next           = " + c_value + "0x",    
 608                 self.next,                                          
 609                 c_none + "
system_mem     = " + c_value + "0x",    
 610                 self.system_mem,                                    
 611                 c_none + "
max_system_mem = " + c_value + "0x",    
 612                 self.max_system_mem, c_none)
 613 
 614 
 615 ################################################################################
 616 class malloc_par:
 617     "python representation of a struct malloc_par"
 618 
 619     def __init__(self, addr=None, mem=None, inferior=None):
 620         self.trim_threshold   = 0
 621         self.top_pad          = 0
 622         self.mmap_threshold   = 0
 623     self.arena_test       = 0
 624     self.arena_max        = 0
 625         self.n_mmaps          = 0
 626         self.n_mmaps_max      = 0
 627         self.max_n_mmaps      = 0
 628         self.no_dyn_threshold = 0
 629         self.mmapped_mem      = 0
 630         self.max_mmapped_mem  = 0
 631         self.max_total_mem    = 0
 632         self.sbrk_base        = 0
 633 
 634         if addr == None:
 635             if mem == None:
 636                 sys.stdout.write(c_error)
 637                 print("Please specify a struct malloc_par address.")
 638                 sys.stdout.write(c_none)
 639                 return None
 640 
 641             self.address = None
 642         else:
 643             self.address = addr
 644 
 645         if inferior == None and mem == None:
 646             inferior = get_inferior()
 647             if inferior == -1:
 648                 return None
 649 
 650         if mem == None:
 651             # a string of raw memory was not provided
 652             try:
 653                 if SIZE_SZ == 4:
 654                     mem = inferior.read_memory(addr, 0x34)
 655                 elif SIZE_SZ == 8:
 656                     mem = inferior.read_memory(addr, 0x58)
 657             except TypeError:
 658                 print(c_error + "Invalid address specified." + c_none)
 659                 return None
 660             except RuntimeError:
 661                 print(c_error + "Could not read address 0x%x" % addr + c_none)
 662                 return None
 663 
 664         if SIZE_SZ == 4:
 665             (self.trim_threshold, 
 666             self.top_pad,         
 667             self.mmap_threshold,  
 668         self.arena_test,      
 669         self.arena_max,       
 670             self.n_mmaps,         
 671             self.n_mmaps_max,     
 672             self.max_n_mmaps,     
 673             self.no_dyn_threshold,
 674             self.mmapped_mem,     
 675             self.max_mmapped_mem, 
 676             self.max_total_mem,   
 677             self.sbrk_base)       = struct.unpack("<13I", mem)
 678         elif SIZE_SZ == 8:
 679             (self.trim_threshold, 
 680             self.top_pad,         
 681             self.mmap_threshold,  
 682         self.arena_test,      
 683         self.arena_max,       
 684             self.n_mmaps,         
 685             self.n_mmaps_max,     
 686             self.max_n_mmaps,     
 687             self.no_dyn_threshold,
 688             self.mmapped_mem,     
 689             self.max_mmapped_mem, 
 690             self.max_total_mem,   
 691             self.sbrk_base)       = struct.unpack("<5Q4I4Q", mem)
 692 
 693     def __str__(self):
 694         return "%s%s%lx%s%lx%s%lx%s%x%s%x%s%x%s%x%s%lx%s%lx%s%lx%s%lx%s" % 
 695                 (c_title + "struct malloc_par {",                  
 696                 c_none + "
trim_threshold   = " + c_value + "0x", 
 697                 self.trim_threshold,                               
 698                 c_none + "
top_pad          = " + c_value + "0x", 
 699                 self.top_pad,                                      
 700                 c_none + "
mmap_threshold   = " + c_value + "0x", 
 701                 self.mmap_threshold,                               
 702                 c_none + "
n_mmaps          = " + c_value + "0x", 
 703                 self.n_mmaps,                                      
 704                 c_none + "
n_mmaps_max      = " + c_value + "0x", 
 705                 self.n_mmaps_max,                                  
 706                 c_none + "
max_n_mmaps      = " + c_value + "0x", 
 707                 self.max_n_mmaps,                                  
 708                 c_none + "
no_dyn_threshold = " + c_value + "0x", 
 709                 self.no_dyn_threshold,                             
 710                 c_none + "
mmapped_mem      = " + c_value + "0x", 
 711                 self.mmapped_mem,                                  
 712                 c_none + "
max_mmapped_mem  = " + c_value + "0x", 
 713                 self.max_mmapped_mem,                              
 714                 c_none + "
max_total_mem    = " + c_value + "0x", 
 715                 self.max_total_mem,                                
 716                 c_none + "
sbrk_base        = " + c_value + "0x", 
 717                 self.sbrk_base,                                    
 718                 c_none)
 719 
 720 
 721 
 722 ################################################################################
 723 # ARENA CONSTANTS AND MACROS
 724 ################################################################################
 725 
 726 HEAP_MIN_SIZE     = 32 * 1024
 727 HEAP_MAX_SIZE     = 1024 * 1024
 728 
 729 def top(ar_ptr):
 730     return ar_ptr.top
 731 
 732 def heap_for_ptr(ptr):
 733     "find the heap and corresponding arena for a given ptr"
 734     return (ptr & ~(HEAP_MAX_SIZE-1))
 735 
 736 
 737 ################################################################################
 738 # GDB PRETTY PRINTERS
 739 ################################################################################
 740 
 741 class malloc_par_printer:
 742     "pretty print the malloc parameters (mp_)"
 743 
 744     def __init__(self, val):
 745         self.val = val
 746 
 747     def to_string(self):
 748         return "%s%s%lx%s%lx%s%lx%s%x%s%x%s%x%s%x%s%lx%s%lx%s%lx%s%lx%s" % 
 749                 (c_title + "struct malloc_par {",                  
 750                 c_none + "
trim_threshold   = " + c_value + "0x", 
 751                 self.val['trim_threshold'],                        
 752                 c_none + "
top_pad          = " + c_value + "0x", 
 753                 self.val['top_pad'],                               
 754                 c_none + "
mmap_threshold   = " + c_value + "0x", 
 755                 self.val['mmap_threshold'],                        
 756                 c_none + "
n_mmaps          = " + c_value + "0x", 
 757                 self.val['n_mmaps'],                               
 758                 c_none + "
n_mmaps_max      = " + c_value + "0x", 
 759                 self.val['n_mmaps_max'],                           
 760                 c_none + "
max_n_mmaps      = " + c_value + "0x", 
 761                 self.val['max_n_mmaps'],                           
 762                 c_none + "
no_dyn_threshold = " + c_value + "0x", 
 763                 self.val['no_dyn_threshold'],                      
 764                 c_none + "
mmapped_mem      = " + c_value + "0x", 
 765                 self.val['mmapped_mem'],                           
 766                 c_none + "
max_mmapped_mem  = " + c_value + "0x", 
 767                 self.val['max_mmapped_mem'],                       
 768                 c_none + "
max_total_mem    = " + c_value + "0x", 
 769                 self.val['max_total_mem'],                         
 770                 c_none + "
sbrk_base        = " + c_value + "0x", 
 771                 self.val['sbrk_base'],                             
 772                 c_none)
 773 
 774     def display_string(self):
 775         return "string"
 776 
 777 ################################################################################
 778 class malloc_state_printer:
 779     "pretty print a struct malloc_state (ar_ptr)"
 780 
 781     def __init__(self, val):
 782         self.val = val
 783 
 784     def to_string(self):
 785         return "%s%s%x%s%x%s%s%lx%s%lx%s%s%s%lx%s%lx%s%lx%s" %      
 786                 (c_title + "struct malloc_state {",                 
 787                 c_none + "
mutex          = " + c_value + "0x",    
 788                 self.val['mutex'],                                  
 789                 c_none + "
flags          = " + c_value + "0x",    
 790                 self.val['flags'],                                  
 791                 c_none + "
fastbinsY      = " + c_value + "{...}", 
 792                 c_none + "
top            = " + c_value + "0x",    
 793                 self.val['top'],                                    
 794                 c_none + "
last_remainder = " + c_value + "0x",    
 795                 self.val['last_remainder'],                         
 796                 c_none + "
bins           = " + c_value + "{...}", 
 797                 c_none + "
binmap         = " + c_value + "{...}", 
 798                 c_none + "
next           = " + c_value + "0x",    
 799                 self.val['next'],                                   
 800                 c_none + "
system_mem     = " + c_value + "0x",    
 801                 self.val['system_mem'],                             
 802                 c_none + "
max_system_mem = " + c_value + "0x",    
 803                 self.val['max_system_mem'],                         
 804                 c_none)
 805 
 806     def display_string(self):
 807         return "string"
 808 
 809 ################################################################################
 810 class malloc_chunk_printer:
 811     "pretty print a struct malloc_chunk"
 812 
 813     def __init__(self, val):
 814         self.val = val
 815 
 816     def to_string(self):
 817         return "%s%s%x%s%x%s%lx%s%lx%s%lx%s%lx%s" %           
 818                 (c_title + "struct malloc_chunk {",           
 819                 c_none + "
prev_size   = " + c_value + "0x", 
 820                 self.val['prev_size'],                        
 821                 c_none + "
size        = " + c_value + "0x", 
 822                 self.val['size'],                             
 823                 c_none + "
fd          = " + c_value + "0x", 
 824                 self.val['fd'],                               
 825                 c_none + "
bk          = " + c_value + "0x", 
 826                 self.val['bk'],                               
 827                 c_none + "
fd_nextsize = " + c_value + "0x", 
 828                 self.val['fd_nextsize'],                      
 829                 c_none + "
bk_nextsize = " + c_value + "0x", 
 830                 self.val['bk_nextsize'],                      
 831                 c_none)
 832 
 833     def display_string(self):
 834         return "string"
 835 
 836 ################################################################################
 837 class heap_info_printer:
 838     "pretty print a struct heap_info"
 839 
 840     def __init__(self, val):
 841         self.val = val
 842 
 843     def to_string(self):
 844         return "%s%s%lx%s%lx%s%lx%s%lx%s" %                     
 845                 (c_title + "struct heap_info {",                
 846                 c_none + "
ar_ptr        = " + c_value + "0x", 
 847                 self.val['ar_ptr'],                             
 848                 c_none + "
prev          = " + c_value + "0x", 
 849                 self.val['prev'],                               
 850                 c_none + "
size          = " + c_value + "0x", 
 851                 self.val['size'],                               
 852                 c_none + "
mprotect_size = " + c_value + "0x", 
 853                 self.val['mprotect_size'],                      
 854                 c_none)
 855 
 856     def display_string(self):
 857         return "string"
 858 
 859 ################################################################################
 860 def pretty_print_heap_lookup(val):
 861     "Look-up and return a pretty-printer that can print val."
 862 
 863     # Get the type.
 864     type = val.type
 865 
 866     # If it points to a reference, get the reference.
 867     if type.code == gdb.TYPE_CODE_REF:
 868         type = type.target()
 869 
 870     # Get the unqualified type, stripped of typedefs.
 871     type = type.unqualified().strip_typedefs()
 872 
 873     # Get the type name.
 874     typename = type.tag
 875     if typename == None:
 876         return None
 877     elif typename == "malloc_par":
 878         return malloc_par_printer(val)
 879     elif typename == "malloc_state":
 880         return malloc_state_printer(val)
 881     elif typename == "malloc_chunk":
 882         return malloc_chunk_printer(val)
 883     elif typename == "_heap_info":
 884         return heap_info_printer(val)
 885     else:
 886         print(typename)
 887 
 888     # Cannot find a pretty printer.  Return None.
 889     return None
 890 
 891 
 892 ################################################################################
 893 # GDB COMMANDS
 894 ################################################################################
 895 
 896 class print_malloc_stats(gdb.Command):
 897     "print general malloc stats, adapted from malloc.c mSTATs()"
 898 
 899     def __init__(self):
 900         super(print_malloc_stats, self).__init__("print_mstats",
 901                                         gdb.COMMAND_DATA, gdb.COMPLETE_NONE)
 902 
 903     def invoke(self, arg, from_tty):
 904         "Specify an optional arena addr: print_mstats main_arena=0x12345"
 905 
 906         try:
 907             mp         = gdb.selected_frame().read_var('mp_')
 908 
 909             if arg.find("main_arena") == -1:
 910                 main_arena = gdb.selected_frame().read_var('main_arena')
 911                 main_arena_address = main_arena.address
 912             else:
 913                 arg = arg.split()
 914                 for item in arg:
 915                     if item.find("main_arena") != -1:
 916                         if len(item) < 12:
 917                             sys.stdout.write(c_error)
 918                             print("Malformed main_arena parameter")
 919                             sys.stdout.write(c_none)
 920                             return
 921                         else:
 922                             main_arena_address = int(item[11:],16)
 923         except RuntimeError:
 924             sys.stdout.write(c_error)
 925             print("No frame is currently selected.")
 926             sys.stdout.write(c_none)
 927             return
 928         except ValueError:
 929             sys.stdout.write(c_error)
 930             print("Debug glibc was not found.")
 931             sys.stdout.write(c_none)
 932             return
 933 
 934         if main_arena_address == 0:
 935             sys.stdout.write(c_error)
 936             print("Invalid main_arena address (0)")
 937             sys.stdout.write(c_none)
 938             return
 939 
 940         in_use_b = mp['mmapped_mem']
 941         system_b = in_use_b
 942 
 943         arena = 0
 944         while(1):
 945             ar_ptr = malloc_state(main_arena_address)
 946             mutex_lock(ar_ptr)
 947 
 948             sys.stdout.write(c_title)
 949             print("=================================", end=' ')
 950             print("Malloc Stats =================================
")
 951             sys.stdout.write(c_none)
 952 
 953             # account for top
 954             avail = chunksize(malloc_chunk(top(ar_ptr), inuse=True, 
 955                     read_data=False))
 956             nblocks = 1
 957 
 958             nfastblocks = 0
 959             fastavail = 0
 960 
 961             # traverse fastbins
 962             for i in range(NFASTBINS):
 963                 p = fastbin(ar_ptr, i)
 964                 while p!=0:
 965                     p = malloc_chunk(p, inuse=False)
 966                     nfastblocks += 1
 967                     fastavail += chunksize(p)
 968                     p = p.fd
 969 
 970             avail += fastavail
 971 
 972             # traverse regular bins
 973             for i in range(1, NBINS):
 974                 b = bin_at(ar_ptr, i)
 975                 p = malloc_chunk(first(malloc_chunk(b,inuse=False)),inuse=False)
 976 
 977                 while p.address != b:
 978                     nblocks += 1
 979                     avail += chunksize(p)
 980                     p = malloc_chunk(first(p), inuse=False)
 981 
 982             sys.stdout.write(c_header)
 983             print("Arena %d:" % arena)
 984             sys.stdout.write(c_none)
 985             print(c_none + "system bytes     = " + 
 986                     c_value + "0x%x" % ar_ptr.system_mem)
 987             print(c_none + "in use bytes     = " + 
 988                     c_value + "0x%x
" % (ar_ptr.system_mem - avail))
 989 
 990             system_b += ar_ptr.system_mem
 991             in_use_b += (ar_ptr.system_mem - avail)
 992 
 993             mutex_unlock(ar_ptr)
 994             if ar_ptr.next == main_arena_address:
 995                 break
 996             else:
 997                 ar_ptr = malloc_state(ar_ptr.next)
 998                 arena += 1
 999 
1000         print(c_header + "Total (including mmap):")
1001         print(c_none + "system bytes     = " + c_value + "0x%x" % system_b)
1002         print(c_none + "in use bytes     = " + c_value + "0x%x" % in_use_b)
1003         print(c_none + "max system bytes = " + 
1004                 c_value + "0x%x" % mp['max_total_mem'])
1005         print(c_none + "max mmap regions = " + 
1006                 c_value + "0x%x" % mp['max_n_mmaps'])
1007         print(c_none + "max mmap bytes   = " + 
1008                 c_value + "0x%lx" % mp['max_mmapped_mem'] + c_none)
1009 
1010 
1011 ################################################################################
1012 class heap(gdb.Command):
1013     "print a comprehensive view of the heap"
1014 
1015     def __init__(self):
1016         super(heap, self).__init__("heap", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)
1017 
1018     def invoke(self, arg, from_tty):
1019         "Usage can be obtained via heap -h"
1020 
1021         inferior = get_inferior()
1022         if inferior == -1:
1023             return
1024 
1025         if arg.find("-h") != -1:
1026             print(c_title + "==============================", end=' ')
1027             print("Heap Dump Help ==================================
" + c_none)
1028 
1029             print(c_title + "Options:
" + c_none)
1030             print(c_header + "  -a 0x1234" + c_none 
1031                     + "	Specify an arena address")
1032             print(c_header + "  -b" + c_none + 
1033                     "		Print compact bin listing (only free chunks)")
1034             print(c_header + "  -c" + c_none + 
1035                     "		Print compact arena listing (all chunks)")
1036             print(c_header + "  -f [#]" + c_none + 
1037                     "	Print all fast bins, or only a single fast bin")
1038             print(c_header + "  -l" + c_none + 
1039                     "		Print a flat listing of all chunks in an arena")
1040             print(c_header + "  -s [#]" + c_none + 
1041                     "	Print all small bins, or only a single small bin
")
1042             return
1043 
1044         a_found = f_found = s_found = p_fb = p_sb = p_b = p_l = p_c = 0
1045         for item in arg.split():
1046             if a_found == 1:
1047                 arena_address = int(item,16)
1048                 a_found = 0
1049                 continue
1050             if f_found == 1:
1051                 f_found = 0
1052                 try:
1053                     fb_number = int(item)
1054                 except:
1055                     pass
1056                 continue
1057             if s_found == 1:
1058                 s_found = 0
1059                 try:
1060                     sb_number = int(item)
1061                 except:
1062                     pass
1063                 continue
1064             if item.find("-a") != -1:
1065                 a_found = 1
1066             if item.find("f") != -1:
1067                 f_found = 1
1068                 fb_number = None
1069                 p_fb = 1
1070             if item.find("s") != -1:
1071                 s_found = 1
1072                 sb_number = None
1073                 p_sb = 1
1074             if item.find("b") != -1:
1075                 p_b = 1
1076             if item.find("l") != -1:
1077                 p_l = 1
1078             if item.find("c") != -1:
1079                 p_c = 1
1080 
1081         if arg.find("-a") == -1:
1082             try:
1083                 main_arena = gdb.selected_frame().read_var('main_arena')
1084                 arena_address = main_arena.address
1085             except RuntimeError:
1086                 print(c_error + "No gdb frame is currently selected." + c_none)
1087                 return
1088             except ValueError:
1089                 print(c_error + "Debug glibc was not found, " 
1090                     "guessing main_arena address via offset from libc." + c_none)
1091 
1092                 #find heap by offset from end of libc in /proc
1093                 libc_end,heap_begin = read_proc_maps(inferior.pid)
1094 
1095                 if SIZE_SZ == 4:
1096                     #__malloc_initialize_hook + 0x20
1097                     #offset seems to be +0x380 on debug glibc, +0x3a0 otherwise
1098                     arena_address = libc_end + 0x3a0
1099                 elif SIZE_SZ == 8:
1100                     #offset seems to be +0xe80 on debug glibc, +0xea0 otherwise
1101                     arena_address = libc_end + 0xea0
1102 
1103                 if libc_end == -1:
1104                     print(c_error + "Invalid address read via /proc" + c_none)
1105                     return
1106 
1107         if arena_address == 0:
1108             print(c_error + "Invalid arena address (0)" + c_none)
1109             return
1110         ar_ptr = malloc_state(arena_address)
1111 
1112         if len(arg) == 0:
1113             if ar_ptr.next == 0:
1114                 print("%s%s %s 0x%x) %s" % (c_error, 
1115                         "ERROR: No arenas could be correctly guessed.", 
1116                         "(Nothing was found at", ar_ptr.address, c_none))
1117                 return
1118 
1119             print(c_title + "==================================", end=' ')
1120             print("Heap Dump ===================================
" + c_none)
1121 
1122             print(c_title + "Arena(s) found:" + c_none)
1123             try: #arena address obtained via read_var
1124                 print("	 arena @ 0x%x" % 
1125                         ar_ptr.address.cast(gdb.lookup_type("unsigned long")))
1126             except: #arena address obtained via -a
1127                 print("	 arena @ 0x%x" % ar_ptr.address)
1128 
1129             if ar_ptr.address != ar_ptr.next:
1130         print('no more than one arena')
1131                 #we have more than one arena
1132 
1133                 curr_arena = malloc_state(ar_ptr.next)
1134                 while (ar_ptr.address != curr_arena.address):
1135                     print("	 arena @ 0x%x" % curr_arena.address)
1136                     curr_arena = malloc_state(curr_arena.next)
1137 
1138                     if curr_arena.address == 0:
1139                         print(c_error + 
1140                            "ERROR: No arenas could be correctly found." + c_none)
1141                         break #breaking infinite loop
1142 
1143             print("")
1144             return
1145 
1146         try:
1147             fb_base = ar_ptr.address.cast(gdb.lookup_type("unsigned long")) + 8
1148         except Exception, e:
1149         print(e)
1150             fb_base = ar_ptr.address + 8
1151         if SIZE_SZ == 4:
1152             try:
1153                 sb_base=ar_ptr.address.cast(gdb.lookup_type("unsigned long"))+56
1154             except:
1155                 sb_base = ar_ptr.address + 56
1156         elif SIZE_SZ == 8:
1157             try:
1158                 sb_base = ar_ptr.address.cast(gdb.lookup_type("unsigned long"))
1159                         + 104
1160             except:
1161                 sb_base = ar_ptr.address + 104
1162 
1163         try:
1164             mp_ = gdb.selected_frame().read_var('mp_')
1165             mp_address = mp_.address
1166         except RuntimeError:
1167             print(c_error + "No gdb frame is currently selected." + c_none)
1168             return
1169         except ValueError:
1170             print(c_error + "Debug glibc was not found, " 
1171                    "guessing mp_ address via offset from main_arena." + c_none)
1172 
1173             if SIZE_SZ == 4:
1174                 try:
1175                     mp_address = ar_ptr.address.cast(gdb.lookup_type(
1176                                                     "unsigned long")) + 0x460
1177                 except:
1178                     mp_address = ar_ptr.address + 0x460
1179             elif SIZE_SZ == 8: #offset 0x880 untested on 64bit
1180                 try:
1181                     mp_address = ar_ptr.address.cast(gdb.lookup_type(
1182                                                     "unsigned long")) + 0x880
1183                 except:
1184                     mp_address = ar_ptr.address + 0x460
1185         sbrk_base = malloc_par(mp_address).sbrk_base
1186 
1187         if p_fb:
1188             print_fastbins(inferior, fb_base, fb_number)
1189             print("")
1190         if p_sb:
1191             print_smallbins(inferior, sb_base, sb_number)
1192             print("")
1193         if p_b:
1194             print_bins(inferior, fb_base, sb_base)
1195             print("")
1196         if p_l:
1197             print_flat_listing(ar_ptr, sbrk_base)
1198             print("")
1199         if p_c:
1200             print_compact_listing(ar_ptr, sbrk_base)
1201             print("")
1202 
1203 
1204 ############################################################################
1205 def read_proc_maps(pid):
1206     '''
1207     Locate the stack of a process using /proc/pid/maps.
1208     Will not work on hardened machines (grsec).
1209     '''
1210 
1211     filename = '/proc/%d/maps' % pid
1212 
1213     try:
1214         fd = open(filename)
1215     except IOError:
1216         print(c_error + "Unable to open %s" % filename + c_none)
1217         return -1,-1
1218 
1219     found = libc_begin = libc_end = heap_begin = heap_end = 0
1220     for line in fd:
1221         if line.find("libc-") != -1:
1222             fields = line.split()
1223 
1224             libc_begin,libc_end = fields[0].split('-')
1225             libc_begin = int(libc_begin,16)
1226             libc_end = int(libc_end,16)
1227         elif line.find("heap") != -1:
1228             fields = line.split()
1229 
1230             heap_begin,heap_end= fields[0].split('-')
1231             heap_begin = int(heap_begin,16)
1232             heap_end = int(heap_end,16)
1233 
1234     fd.close()
1235 
1236     if libc_begin==0 or libc_end==0:
1237         print(c_error+"Unable to read libc address information via /proc"+c_none)
1238         return -1,-1
1239 
1240     if heap_begin==0 or heap_end==0:
1241         print(c_error+"Unable to read heap address information via /proc"+c_none)
1242         return -1,-1
1243 
1244     return libc_end,heap_begin
1245 
1246 
1247 ################################################################################
1248 def print_fastbins(inferior, fb_base, fb_num):
1249     "walk and print the fast bins"
1250 
1251     print(c_title + "===================================", end=' ')
1252     print("Fastbins ===================================
" + c_none)
1253 
1254     for fb in range(0,NFASTBINS):
1255         if fb_num != None:
1256             fb = fb_num
1257 
1258         offset = fb_base + fb*SIZE_SZ
1259         try:
1260             mem = inferior.read_memory(offset, SIZE_SZ)
1261             if SIZE_SZ == 4:
1262                 fd = struct.unpack("<I", mem)[0]
1263             elif SIZE_SZ == 8:
1264                 fd = struct.unpack("<Q", mem)[0]
1265         except RuntimeError:
1266             print(c_error + " ERROR: Invalid fb addr 0x%lx" % offset + c_none)
1267             return
1268 
1269         print("%s%s%d%s%s0x%08lx%s%s%s0x%08lx%s%s" % 
1270                 (c_header,"[ fb  ",fb," ] ",c_none,offset,
1271                  " -> ",c_value,"[ ",fd," ]",c_none), end=' ')
1272 
1273         if fd == 0: #fastbin is empty
1274             print("")
1275         else:
1276             fb_size = ((MIN_CHUNK_SIZE) +(MALLOC_ALIGNMENT)*fb)
1277             print("(%d)" % fb_size)
1278             chunk = malloc_chunk(fd, inuse=False)
1279             while chunk.fd != 0:
1280                 if chunk.fd is None:   # could not read memory section
1281                     break
1282                 print("%s%26s0x%08lx%s%s(%d)" % (c_value,"[ ",chunk.fd," ] ",c_none, fb_size))
1283                 chunk = malloc_chunk(chunk.fd, inuse=False)
1284 
1285         if fb_num != None: #only print one fastbin
1286             return
1287 
1288 
1289 ################################################################################
1290 def print_smallbins(inferior, sb_base, sb_num):
1291     "walk and print the small bins"
1292 
1293     print(c_title + "===================================", end=' ')
1294     print("Smallbins ==================================
" + c_none)
1295 
1296     for sb in range(2,NBINS+2,2):
1297         if sb_num != None and sb_num!=0:
1298             sb = sb_num*2
1299 
1300         offset = sb_base + (sb-2)*SIZE_SZ
1301         try:
1302             mem = inferior.read_memory(offset, 2*SIZE_SZ)
1303             if SIZE_SZ == 4:
1304                 fd,bk = struct.unpack("<II", mem)
1305             elif SIZE_SZ == 8:
1306                 fd,bk = struct.unpack("<QQ", mem)
1307         except RuntimeError:
1308             print(c_error + " ERROR: Invalid sb addr 0x%lx" % offset + c_none)
1309             return
1310 
1311         print("%s%s%02d%s%s0x%08lx%s%s%s0x%08lx%s0x%08lx%s%s" % 
1312                             (c_header,"[ sb ",sb/2," ] ",c_none,offset, 
1313                             " -> ",c_value,"[ ", fd, " | ", bk, " ] ",  
1314                             c_none))
1315 
1316         while (1):
1317             if fd == (offset-2*SIZE_SZ):
1318                 break
1319 
1320             chunk = malloc_chunk(fd, inuse=False)
1321             print("%s%26s0x%08lx%s0x%08lx%s%s" % 
1322                     (c_value,"[ ",chunk.fd," | ",chunk.bk," ] ",c_none), end=' ')
1323             print("(%d)" % chunksize(chunk))
1324 
1325             fd = chunk.fd
1326 
1327         if sb_num != None: #only print one smallbin
1328             return
1329 
1330 
1331 ################################################################################
1332 def print_bins(inferior, fb_base, sb_base):
1333     "walk and print the nonempty free bins, modified from jp"
1334 
1335     print(c_title + "==================================", end=' ')
1336     print("Heap Dump ===================================
" + c_none)
1337 
1338     for fb in range(0,NFASTBINS):
1339         print_once = True
1340         p = malloc_chunk(fb_base-(2*SIZE_SZ)+fb*SIZE_SZ, inuse=False)
1341 
1342         while (p.fd != 0):
1343             if p.fd is None:
1344                 break
1345 
1346             if print_once:
1347                 print_once = False
1348                 print(c_header + "  fast bin %d   @ 0x%lx" % 
1349                         (fb,p.fd) + c_none)
1350             print("    free chunk @ " + c_value + "0x%lx" % p.fd + c_none + 
1351                   " - size" + c_value, end=' ')
1352             p = malloc_chunk(p.fd, inuse=False)
1353             print("0x%lx" % chunksize(p) + c_none)
1354 
1355     for i in range(1, NBINS):
1356         print_once = True
1357         b = sb_base + i*2*SIZE_SZ - 4*SIZE_SZ
1358         p = malloc_chunk(first(malloc_chunk(b, inuse=False)), inuse=False)
1359 
1360         while p.address != b:
1361             if print_once:
1362                 print_once = False
1363                 if i==1:
1364                     try:
1365                         print(c_header + "  unsorted bin @ 0x%lx" % 
1366                           (b.cast(gdb.lookup_type("unsigned long")) 
1367                           + 2*SIZE_SZ) + c_none)
1368                     except:
1369                         print(c_header + "  unsorted bin @ 0x%lx" % 
1370                           (b + 2*SIZE_SZ) + c_none)
1371                 else:
1372                     try:
1373                         print(c_header + "  small bin %d @ 0x%lx" %  
1374                          (i,b.cast(gdb.lookup_type("unsigned long")) 
1375                          + 2*SIZE_SZ) + c_none)
1376                     except:
1377                         print(c_header + "  small bin %d @ 0x%lx" % 
1378                          (i,b + 2*SIZE_SZ) + c_none)
1379 
1380             print(c_none + "    free_chunk @ " + c_value 
1381                   + "0x%lx " % p.address + c_none        
1382                   + "- size " + c_value + "0x%lx" % chunksize(p) + c_none)
1383 
1384             p = malloc_chunk(first(p), inuse=False)
1385 
1386 
1387 ################################################################################
1388 def print_flat_listing(ar_ptr, sbrk_base):
1389     "print a flat listing of an arena, modified from jp and arena.c"
1390 
1391     print(c_title + "==================================", end=' ')
1392     print("Heap Dump ===================================
" + c_none)
1393     print("%s%14s%17s%15s%s" % (c_header, "ADDR", "SIZE", "STATUS", c_none))
1394     print("sbrk_base " + c_value + "0x%lx" % sbrk_base)
1395 
1396     p = malloc_chunk(sbrk_base, inuse=True, read_data=False)
1397 
1398     while(1):
1399         print("%schunk     %s0x%-14lx 0x%-10lx%s" % 
1400                 (c_none, c_value, p.address, chunksize(p), c_none), end=' ')
1401 
1402         if p.address == top(ar_ptr):
1403             print("(top)")
1404             break
1405         elif p.size == (0|PREV_INUSE):
1406             print("(fence)")
1407             break
1408 
1409         if inuse(p):
1410             print("%s" % "(inuse)")
1411         else:
1412             p = malloc_chunk(p.address, inuse=False)
1413             print("(F) FD %s0x%lx%s BK %s0x%lx%s" % 
1414                     (c_value, p.fd, c_none,c_value,p.bk,c_none), end=' ')
1415 
1416             if ((p.fd == ar_ptr.last_remainder) 
1417             and (p.bk == ar_ptr.last_remainder) 
1418             and (ar_ptr.last_remainder != 0)):
1419                 print("(LR)")
1420             elif ((p.fd == p.bk) & ~inuse(p)):
1421                 print("(LC)")
1422             else:
1423                 print("")
1424 
1425         p = malloc_chunk(next_chunk(p), inuse=True, read_data=False)
1426 
1427     print(c_none + "sbrk_end  " + c_value 
1428             + "0x%lx" % (sbrk_base + ar_ptr.system_mem) + c_none)
1429 
1430 
1431 ################################################################################
1432 def print_compact_listing(ar_ptr, sbrk_base):
1433     "print a compact layout of the heap, modified from jp"
1434 
1435     print(c_title + "==================================", end=' ')
1436     print("Heap Dump ===================================" + c_none)
1437     p = malloc_chunk(sbrk_base, inuse=True, read_data=False)
1438 
1439     while(1):
1440         if p.address == top(ar_ptr):
1441             sys.stdout.write("|T|
")
1442             break
1443 
1444         if inuse(p):
1445             sys.stdout.write("|A|")
1446         else:
1447             p = malloc_chunk(p.address, inuse=False)
1448 
1449             if ((p.fd == ar_ptr.last_remainder) 
1450             and (p.bk == ar_ptr.last_remainder) 
1451             and (ar_ptr.last_remainder != 0)):
1452                 sys.stdout.write("|L|")
1453             else:
1454                 sys.stdout.write("|%d|" % bin_index(p.size))
1455 
1456         p = malloc_chunk(next_chunk(p), inuse=True, read_data=False)
1457 
1458 
1459 ################################################################################
1460 class print_bin_layout(gdb.Command):
1461     "dump the layout of a free bin"
1462 
1463     def __init__(self):
1464         super(print_bin_layout, self).__init__("print_bin_layout",
1465                                         gdb.COMMAND_DATA, gdb.COMPLETE_NONE)
1466 
1467     def invoke(self, arg, from_tty):
1468         "Specify an optional arena addr: print_bin_layout main_arena=0x12345"
1469 
1470         if len(arg) == 0:
1471             sys.stdout.write(c_error)
1472             print("Please specify the free bin to dump")
1473             sys.stdout.write(c_none)
1474             return
1475 
1476         try:
1477             if arg.find("main_arena") == -1:
1478                 main_arena = gdb.selected_frame().read_var('main_arena')
1479                 main_arena_address = main_arena.address
1480             else:
1481                 arg = arg.split()
1482                 for item in arg:
1483                     if item.find("main_arena") != -1:
1484                         if len(item) < 12:
1485                             sys.stdout.write(c_error)
1486                             print("Malformed main_arena parameter")
1487                             sys.stdout.write(c_none)
1488                             return
1489                         else:
1490                             main_arena_address = int(item[11:],16)
1491         except RuntimeError:
1492             sys.stdout.write(c_error)
1493             print("No frame is currently selected.")
1494             sys.stdout.write(c_none)
1495             return
1496         except ValueError:
1497             sys.stdout.write(c_error)
1498             print("Debug glibc was not found.")
1499             sys.stdout.write(c_none)
1500             return
1501 
1502         if main_arena_address == 0:
1503             sys.stdout.write(c_error)
1504             print("Invalid main_arena address (0)")
1505             sys.stdout.write(c_none)
1506             return
1507 
1508         ar_ptr = malloc_state(main_arena_address)
1509         mutex_lock(ar_ptr)
1510 
1511         sys.stdout.write(c_title)
1512         print("=================================", end=' ')
1513         print("Bin Layout ===================================
")
1514         sys.stdout.write(c_none)
1515 
1516         b = bin_at(ar_ptr, int(arg))
1517         p = malloc_chunk(first(malloc_chunk(b, inuse=False)), inuse=False)
1518         print_once = True
1519         print_str  = ""
1520         count      = 0
1521 
1522         while p.address != b:
1523             if print_once:
1524                 print_once=False
1525                 print_str += "-->  " + c_value + "[bin %d]" % int(arg) + c_none
1526                 count += 1
1527 
1528             print_str += "  <-->  " + c_value + "0x%lx" % p.address + c_none
1529             count += 1
1530             #print_str += "  <-->  0x%lx" % p.address
1531             p = malloc_chunk(first(p), inuse=False)
1532 
1533         if len(print_str) != 0:
1534             print_str += "  <--"
1535             print(print_str)
1536             print("%s%s%s" % ("|"," " * (len(print_str) - 2 - count*12),"|"))
1537             print("%s" % ("-" * (len(print_str) - count*12)))
1538         else:
1539             print("Bin %d empty." % int(arg))
1540 
1541         mutex_unlock(ar_ptr)
1542 
1543 
1544 ################################################################################
1545 class check_house_of_mind(gdb.Command):
1546     "print and help validate a house of mind layout"
1547 
1548     def __init__(self):
1549         super(check_house_of_mind, self).__init__("check_house_of_mind",
1550                                         gdb.COMMAND_DATA, gdb.COMPLETE_NONE)
1551 
1552     def invoke(self, arg, from_tty):
1553         """
1554         Specify the house of mind method and chunk address (p=mem2chunk(mem)):
1555         check_house_of_mind method=unsortedbin p=0x12345678
1556         check_house_of_mind method=fastbin p=0x12345678
1557         """
1558 
1559         if arg.find("method") == -1:
1560             print("Please specify the House of Mind method to use:")
1561             print("house_of_mind method={unsortedbin, fastbin}")
1562             return
1563         elif arg.find("p") == -1:
1564             print("Please specify the chunk address to use:")
1565             print("house_of_mind p=0x12345678")
1566             return
1567         else:
1568             arg = arg.split()
1569             for item in arg:
1570                 if item.find("method") != -1:
1571                     if len(item) < 8:
1572                         sys.stdout.write(c_error)
1573                         print("Malformed method parameter")
1574                         print("Please specify the House of Mind method to use:")
1575                         print("house_of_mind method={unsortedbin, fastbin}")
1576                         sys.stdout.write(c_none)
1577                         return
1578                     else:
1579                         method = item[7:]
1580                 if item.find("p") != -1:
1581                     if len(item) < 11:
1582                         sys.stdout.write(c_error)
1583                         print("Malformed chunk parameter")
1584                         print("Please specify the chunk address to use:")
1585                         print("house_of_mind p=0x12345678")
1586                         sys.stdout.write(c_none)
1587                         return
1588                     else:
1589                         p = int(item[2:],16)
1590 
1591         sys.stdout.write(c_title)
1592         print("===============================", end=' ')
1593         print("House of Mind ==================================
")
1594         sys.stdout.write(c_none)
1595 
1596         if method.find("unsorted") != -1:
1597             self.unsorted_bin_method(p)
1598         elif method.find("fast") != -1:
1599             self.fast_bin_method(p)
1600 
1601     def unsorted_bin_method(self, p):
1602         p = malloc_chunk(addr=p, inuse=True, read_data=False)
1603 
1604         print(c_none + "Checking chunk p")
1605         print(c_none + " [*] p = " + c_value + "0x%x" % p.address + c_none)
1606 
1607         if p.address < gdb.parse_and_eval("(unsigned int)%d" % -chunksize(p)):
1608             print(" [*] size does not wrap")
1609         else:
1610             print(c_error + " [_] ERROR: p > -size" + c_none)
1611             return
1612 
1613         if chunksize(p) >= MINSIZE:
1614             print(" [*] size is > minimum chunk size")
1615         else:
1616             print(c_error + " [_] ERROR: chunksize(p) < MINSIZE" + c_none)
1617             return
1618 
1619         if chunksize(p) > get_max_fast():
1620             print(" [*] size is not in fastbin range")
1621         else:
1622             print(c_error + " [_] ERROR: size is in fastbin range" + c_none)
1623             return
1624 
1625         if not chunk_is_mmapped(p):
1626             print(" [*] is_mmapped bit is not set")
1627         else:
1628             print(c_error + " [_] ERROR: IS_MMAPPED bit is set" + c_none)
1629             return
1630 
1631         if prev_inuse(p):
1632             print(" [*] prev_inuse bit is set")
1633         else:
1634             print(c_error + " [_] ERROR: PREV_INUSE bit is not set, this will", end=' ')
1635             print("trigger backward consolidation" + c_none)
1636 
1637         if chunk_non_main_arena(p):
1638             print(" [*] non_main_arena flag is set")
1639         else:
1640             print(c_error + " [_] ERROR: p's non_main_arena flag is NOT set")
1641             return
1642 
1643         print(c_none + "
Checking struct heap_info")
1644         print(c_none + " [*] struct heap_info = " 
1645                 + c_value + "0x%x" % heap_for_ptr(p.address))
1646 
1647         inferior = get_inferior()
1648         if inferior == -1:
1649             return None
1650 
1651         try:
1652             mem = inferior.read_memory(heap_for_ptr(p.address), SIZE_SZ)
1653             if SIZE_SZ == 4:
1654                 ar_ptr = struct.unpack("<I", mem)[0]
1655             elif SIZE_SZ == 8:
1656                 ar_ptr = struct.unpack("<Q", mem)[0]
1657         except RuntimeError:
1658             print(c_error + " [_] ERROR: Invalid heap_info address 0x%x" 
1659                     % heap_for_ptr(p.address) + c_none)
1660             return
1661 
1662         print(c_none + " [*] ar_ptr = " + c_value + "0x%x" % ar_ptr)
1663         print(c_none + "
Checking struct malloc_state")
1664 
1665         #test malloc_state address
1666         try:
1667             mutex = inferior.read_memory(ar_ptr, SIZE_SZ)
1668         except RuntimeError:
1669             print(c_error + " [_] ERROR: Invalid malloc_state address 0x%x" % 
1670                     ar_ptr + c_none)
1671             return
1672 
1673         av = malloc_state(ar_ptr)
1674 
1675         if av.mutex == 0:
1676             print(c_none + " [*] av->mutex is zero")
1677         else:
1678             print(c_error + " [_] ERROR: av->mutex is not zero" + c_none)
1679             return
1680 
1681         if p.address != av.top:
1682             print(c_none + " [*] p is not the top chunk")
1683         else:
1684             print(c_error + " [_] ERROR: p is the top chunk" + c_none)
1685             return
1686 
1687         if noncontiguous(av):
1688             print(c_none + " [*] noncontiguous_bit is set")
1689         elif contiguous(av):
1690             print(c_error + 
1691                 " [_] ERROR: noncontiguous_bit is NOT set in av->flags" + c_none)
1692             return
1693 
1694         print(" [*] bck = &av->bins[0] = " + c_value + "0x%x" % (ar_ptr+0x38))
1695 
1696         if SIZE_SZ == 4:
1697             print(c_none + " [*] fwd = bck->fd = *(&av->bins[0] + 8) =", end=' ')
1698         elif SIZE_SZ == 8:
1699             print(c_none + " [*] fwd = bck->fd = *(&av->bins[0] + 16) =", end=' ')
1700 
1701         fwd = inferior.read_memory(ar_ptr + 0x38 + 2*SIZE_SZ, SIZE_SZ)
1702         if SIZE_SZ == 4:
1703             fwd = struct.unpack("<I", fwd)[0]
1704         elif SIZE_SZ == 8:
1705             fwd = struct.unpack("<Q", fwd)[0]
1706         print(c_value + "0x%x" % fwd)
1707 
1708         if fwd != (ar_ptr+0x38):
1709             print(c_none + " [!] fwd->bk (0x%x) != bck (0x%x)" % 
1710                     (fwd, ar_ptr+0x38) + c_error)
1711             print("     - ERROR: This will prevent this attack on glibc 2.11+", end=' ')
1712             print(c_none)
1713 
1714         print(c_none + "
Checking following chunks")
1715         nextchunk = chunk_at_offset(p, chunksize(p))
1716 
1717         if prev_inuse(nextchunk):
1718             print(c_none + " [*] prev_inuse of the next chunk is set")
1719         else:
1720             print(c_error + " [_] PREV_INUSE bit of the next chunk is not set" 
1721                     + c_none)
1722             return
1723 
1724         if chunksize(nextchunk) > 2*SIZE_SZ:
1725             print(c_none + " [*] nextchunk size is > minimum size")
1726         else:
1727             print(c_error + " [_] ERROR: nextchunk size (%d) < %d" % 
1728                     (chunksize(nextchunk), 2*SIZE_SZ) + c_none)
1729             return
1730 
1731         if chunksize(nextchunk) < av.system_mem:
1732             print(c_none + " [*] nextchunk size is < av->system_mem")
1733         else:
1734             print(c_error + " [_] ERROR: nextchunk size (0x%x) >" % 
1735                     chunksize(nextchunk), end=' ')
1736             print("av->system_mem (0x%x)" % av.system_mem + c_none)
1737             return
1738 
1739         if nextchunk.address != av.top:
1740             print(c_none + " [*] nextchunk != av->top")
1741         else:
1742             print(c_error + " [_] ERROR: nextchunk is av->top (0x%x)" % av.top 
1743                     + c_none)
1744             return
1745 
1746         if inuse_bit_at_offset(nextchunk, chunksize(nextchunk)):
1747             print(c_none + " [*] prev_inuse bit set on chunk after nextchunk")
1748         else:
1749             print(c_error + " [_] ERROR: PREV_INUSE bit of chunk after", end=' ')
1750             print("nextchunk (0x%x) is not set" % 
1751                     (nextchunk.address + chunksize(nextchunk)) + c_none)
1752             return
1753 
1754         print(c_header + "
p (0x%x) will be written to fwd->bk (0x%x)" 
1755                 % (p.address, fwd+0xC) + c_none)
1756 
1757     def fast_bin_method(self, p):
1758         p = malloc_chunk(addr=p, inuse=True, read_data=False)
1759 
1760         print(c_none + "Checking chunk p")
1761         print(c_none + " [*] p = " + c_value + "0x%x" % p.address + c_none)
1762 
1763         if p.address < gdb.parse_and_eval("(unsigned int)%d" % -chunksize(p)):
1764             print(" [*] size does not wrap")
1765         else:
1766             print(c_error + " [_] ERROR: p > -size" + c_none)
1767             return
1768 
1769         if chunksize(p) >= MINSIZE:
1770             print(" [*] size is >= minimum chunk size")
1771         else:
1772             print(c_error + " [_] ERROR: chunksize(p) < MINSIZE" + c_none)
1773             return
1774 
1775         if chunksize(p) < get_max_fast():
1776             print(" [*] size is in fastbin range")
1777         else:
1778             print(c_error + " [_] ERROR: size is not in fastbin range" + c_none)
1779             return
1780 
1781         if chunk_non_main_arena(p):
1782             print(" [*] non_main_arena flag is set")
1783         else:
1784             print(c_error + " [_] ERROR: p's non_main_arena flag is NOT set")
1785             return
1786 
1787         if prev_inuse(p):
1788             print(" [*] prev_inuse bit is set")
1789         else:
1790             print(c_error + " [_] ERROR: PREV_INUSE bit is not set, this will", end=' ')
1791             print("trigger backward consolidation" + c_none)
1792 
1793         print(c_none + "
Checking struct heap_info")
1794         print(c_none + " [*] struct heap_info = " 
1795                 + c_value + "0x%x" % heap_for_ptr(p.address))
1796 
1797         inferior = get_inferior()
1798         if inferior == -1:
1799             return None
1800 
1801         try:
1802             mem = inferior.read_memory(heap_for_ptr(p.address), SIZE_SZ)
1803             if SIZE_SZ == 4:
1804                 ar_ptr = struct.unpack("<I", mem)[0]
1805             elif SIZE_SZ == 8:
1806                 ar_ptr = struct.unpack("<Q", mem)[0]
1807         except RuntimeError:
1808             print(c_error + " [_] ERROR: Invalid heap_info address 0x%x" 
1809                     % heap_for_ptr(p.address) + c_none)
1810             return
1811 
1812         print(c_none + " [*] ar_ptr = " + c_value + "0x%x" % ar_ptr)
1813         print(c_none + "
Checking struct malloc_state")
1814 
1815         #test malloc_state address
1816         try:
1817             mutex = inferior.read_memory(ar_ptr, SIZE_SZ)
1818         except RuntimeError:
1819             print(c_error + " [_] ERROR: Invalid malloc_state address 0x%x" % 
1820                     ar_ptr + c_none)
1821             return
1822 
1823         av = malloc_state(ar_ptr)
1824 
1825         if av.mutex == 0:
1826             print(c_none + " [*] av->mutex is zero")
1827         else:
1828             print(c_error + " [_] ERROR: av->mutex is not zero" + c_none)
1829             return
1830 
1831         print(c_none + " [*] av->system_mem is 0x%x" % av.system_mem)
1832 
1833         print(c_none + "
Checking following chunk")
1834         nextchunk = chunk_at_offset(p, chunksize(p))
1835         print(" [*] nextchunk = " + c_value + "0x%x" % nextchunk.address)
1836 
1837         if nextchunk.size > 2*SIZE_SZ:
1838             print(c_none + " [*] nextchunk size is > 2*SIZE_SZ")
1839         else:
1840             print(c_error + " [_] ERROR: nextchunk size is <= 2*SIZE_SZ" +c_none)
1841             return
1842 
1843         if chunksize(nextchunk) < av.system_mem:
1844             print(c_none + " [*] nextchunk size is < av->system_mem")
1845         else:
1846             print(c_error + " [_] ERROR: nextchunk size (0x%x) is >= " % 
1847                     chunksize(nextchunk), end=' ')
1848             print("av->system_mem (0x%x)" % (av.system_mem) + c_none)
1849             return
1850 
1851         fb = ar_ptr + (2*SIZE_SZ) + (fastbin_index(p.size)*SIZE_SZ)
1852         print(c_header + "
p (0x%x) will be written to fb (0x%x)" 
1853                 % (p.address, fb) + c_none)
1854 
1855 
1856 ################################################################################
1857 # INITIALIZE CUSTOM GDB CODE
1858 ################################################################################
1859 heap()
1860 print_malloc_stats()
1861 print_bin_layout()
1862 check_house_of_mind()
1863 gdb.pretty_printers.append(pretty_print_heap_lookup)
View Code
原文地址:https://www.cnblogs.com/wangaohui/p/5534888.html