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 = "\033[31m" 17 c_red_b = "\033[01;31m" 18 c_green = "\033[32m" 19 c_green_b = "\033[01;32m" 20 c_yellow = "\033[33m" 21 c_yellow_b = "\033[01;33m" 22 c_blue = "\033[34m" 23 c_blue_b = "\033[01;34m" 24 c_purple = "\033[35m" 25 c_purple_b = "\033[01;35m" 26 c_teal = "\033[36m" 27 c_teal_b = "\033[01;36m" 28 c_none = "\033[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 + "\nprev_size = " + c_value + "0x", 468 self.prev_size, 469 c_none + "\nsize = " + 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 ("\ndata = " + c_value + str(self.data), 476 c_none + "\nraw = " + c_value, 477 struct.pack("<%dI"%len(self.data), *self.data)) 478 elif SIZE_SZ == 8: 479 ret += "%s%s%r" % 480 ("\ndata = " + c_value + str(self.data), 481 c_none + "\nraw = " + 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 + "\nprev_size = " + c_value + "0x", 490 self.prev_size, 491 c_none + "\nsize = " + c_value + "0x", 492 self.size, 493 c_none + "\nfd = " + c_value + "0x", 494 self.fd, 495 c_none + "\nbk = " + c_value + "0x", 496 self.bk, 497 c_none + "\nfd_nextsize = " + c_value + "0x", 498 self.fd_nextsize, 499 c_none + "\nbk_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 + "\nmutex = " + c_value + "0x", 597 self.mutex, 598 c_none + "\nflags = " + c_value + "0x", 599 self.flags, 600 c_none + "\nfastbinsY = " + c_value + "{...}", 601 c_none + "\ntop = " + c_value + "0x", 602 self.top, 603 c_none + "\nlast_remainder = " + c_value + "0x", 604 self.last_remainder, 605 c_none + "\nbins = " + c_value + "{...}", 606 c_none + "\nbinmap = " + c_value + "{...}", 607 c_none + "\nnext = " + c_value + "0x", 608 self.next, 609 c_none + "\nsystem_mem = " + c_value + "0x", 610 self.system_mem, 611 c_none + "\nmax_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 + "\ntrim_threshold = " + c_value + "0x", 697 self.trim_threshold, 698 c_none + "\ntop_pad = " + c_value + "0x", 699 self.top_pad, 700 c_none + "\nmmap_threshold = " + c_value + "0x", 701 self.mmap_threshold, 702 c_none + "\nn_mmaps = " + c_value + "0x", 703 self.n_mmaps, 704 c_none + "\nn_mmaps_max = " + c_value + "0x", 705 self.n_mmaps_max, 706 c_none + "\nmax_n_mmaps = " + c_value + "0x", 707 self.max_n_mmaps, 708 c_none + "\nno_dyn_threshold = " + c_value + "0x", 709 self.no_dyn_threshold, 710 c_none + "\nmmapped_mem = " + c_value + "0x", 711 self.mmapped_mem, 712 c_none + "\nmax_mmapped_mem = " + c_value + "0x", 713 self.max_mmapped_mem, 714 c_none + "\nmax_total_mem = " + c_value + "0x", 715 self.max_total_mem, 716 c_none + "\nsbrk_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 + "\ntrim_threshold = " + c_value + "0x", 751 self.val[‘trim_threshold‘], 752 c_none + "\ntop_pad = " + c_value + "0x", 753 self.val[‘top_pad‘], 754 c_none + "\nmmap_threshold = " + c_value + "0x", 755 self.val[‘mmap_threshold‘], 756 c_none + "\nn_mmaps = " + c_value + "0x", 757 self.val[‘n_mmaps‘], 758 c_none + "\nn_mmaps_max = " + c_value + "0x", 759 self.val[‘n_mmaps_max‘], 760 c_none + "\nmax_n_mmaps = " + c_value + "0x", 761 self.val[‘max_n_mmaps‘], 762 c_none + "\nno_dyn_threshold = " + c_value + "0x", 763 self.val[‘no_dyn_threshold‘], 764 c_none + "\nmmapped_mem = " + c_value + "0x", 765 self.val[‘mmapped_mem‘], 766 c_none + "\nmax_mmapped_mem = " + c_value + "0x", 767 self.val[‘max_mmapped_mem‘], 768 c_none + "\nmax_total_mem = " + c_value + "0x", 769 self.val[‘max_total_mem‘], 770 c_none + "\nsbrk_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 + "\nmutex = " + c_value + "0x", 788 self.val[‘mutex‘], 789 c_none + "\nflags = " + c_value + "0x", 790 self.val[‘flags‘], 791 c_none + "\nfastbinsY = " + c_value + "{...}", 792 c_none + "\ntop = " + c_value + "0x", 793 self.val[‘top‘], 794 c_none + "\nlast_remainder = " + c_value + "0x", 795 self.val[‘last_remainder‘], 796 c_none + "\nbins = " + c_value + "{...}", 797 c_none + "\nbinmap = " + c_value + "{...}", 798 c_none + "\nnext = " + c_value + "0x", 799 self.val[‘next‘], 800 c_none + "\nsystem_mem = " + c_value + "0x", 801 self.val[‘system_mem‘], 802 c_none + "\nmax_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 + "\nprev_size = " + c_value + "0x", 820 self.val[‘prev_size‘], 821 c_none + "\nsize = " + c_value + "0x", 822 self.val[‘size‘], 823 c_none + "\nfd = " + c_value + "0x", 824 self.val[‘fd‘], 825 c_none + "\nbk = " + c_value + "0x", 826 self.val[‘bk‘], 827 c_none + "\nfd_nextsize = " + c_value + "0x", 828 self.val[‘fd_nextsize‘], 829 c_none + "\nbk_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 + "\nar_ptr = " + c_value + "0x", 847 self.val[‘ar_ptr‘], 848 c_none + "\nprev = " + c_value + "0x", 849 self.val[‘prev‘], 850 c_none + "\nsize = " + c_value + "0x", 851 self.val[‘size‘], 852 c_none + "\nmprotect_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 =================================\n") 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\n" % (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 ==================================\n" + c_none) 1028 1029 print(c_title + "Options:\n" + c_none) 1030 print(c_header + " -a 0x1234" + c_none 1031 + "\tSpecify an arena address") 1032 print(c_header + " -b" + c_none + 1033 "\t\tPrint compact bin listing (only free chunks)") 1034 print(c_header + " -c" + c_none + 1035 "\t\tPrint compact arena listing (all chunks)") 1036 print(c_header + " -f [#]" + c_none + 1037 "\tPrint all fast bins, or only a single fast bin") 1038 print(c_header + " -l" + c_none + 1039 "\t\tPrint a flat listing of all chunks in an arena") 1040 print(c_header + " -s [#]" + c_none + 1041 "\tPrint all small bins, or only a single small bin\n") 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 ===================================\n" + c_none) 1121 1122 print(c_title + "Arena(s) found:" + c_none) 1123 try: #arena address obtained via read_var 1124 print("\t arena @ 0x%x" % 1125 ar_ptr.address.cast(gdb.lookup_type("unsigned long"))) 1126 except: #arena address obtained via -a 1127 print("\t 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("\t 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 ===================================\n" + 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 ==================================\n" + 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 ===================================\n" + 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 ===================================\n" + 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|\n") 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 ===================================\n") 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 ==================================\n") 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 + "\nChecking 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 + "\nChecking 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 + "\nChecking 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 + "\np (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 + "\nChecking 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 + "\nChecking 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 + "\nChecking 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 + "\np (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)
时间: 2024-10-30 16:16:01