1# PEDA - Python Exploit Development Assistance for GDB 2# 3# Copyright (C) 2012 Long Le Dinh <longld at vnsecurity.net> 4# 5# License: see LICENSE file for details 6# 7 8from __future__ import absolute_import 9from __future__ import division 10from __future__ import print_function 11 12import re 13import os 14import sys 15import shlex 16import string 17import time 18import signal 19import traceback 20import codecs 21 22# point to absolute path of peda.py 23PEDAFILE = os.path.abspath(os.path.expanduser(__file__)) 24if os.path.islink(PEDAFILE): 25 PEDAFILE = os.readlink(PEDAFILE) 26sys.path.insert(0, os.path.dirname(PEDAFILE) + "/lib/") 27 28# Use six library to provide Python 2/3 compatibility 29import six 30from six.moves import range 31from six.moves import input 32try: 33 import six.moves.cPickle as pickle 34except ImportError: 35 import pickle 36 37 38 39from skeleton import * 40from shellcode import * 41from utils import * 42import config 43from nasm import * 44 45if sys.version_info.major == 3: 46 from urllib.request import urlopen 47 from urllib.parse import urlencode 48 pyversion = 3 49else: 50 from urllib import urlopen 51 from urllib import urlencode 52 pyversion = 2 53 54REGISTERS = { 55 8 : ["al", "ah", "bl", "bh", "cl", "ch", "dl", "dh"], 56 16: ["ax", "bx", "cx", "dx"], 57 32: ["eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp", "eip"], 58 64: ["rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "rsp", "rip", 59 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"] 60} 61 62########################################################################### 63class PEDA(object): 64 """ 65 Class for actual functions of PEDA commands 66 """ 67 def __init__(self): 68 self.SAVED_COMMANDS = {} # saved GDB user's commands 69 70 71 #################################### 72 # GDB Interaction / Misc Utils # 73 #################################### 74 def execute(self, gdb_command): 75 """ 76 Wrapper for gdb.execute, catch the exception so it will not stop python script 77 78 Args: 79 - gdb_command (String) 80 81 Returns: 82 - True if execution succeed (Bool) 83 """ 84 try: 85 gdb.execute(gdb_command) 86 return True 87 except Exception as e: 88 if config.Option.get("debug") == "on": 89 msg('Exception (%s): %s' % (gdb_command, e), "red") 90 traceback.print_exc() 91 return False 92 93 def execute_redirect(self, gdb_command, silent=False): 94 """ 95 Execute a gdb command and capture its output 96 97 Args: 98 - gdb_command (String) 99 - silent: discard command's output, redirect to /dev/null (Bool) 100 101 Returns: 102 - output of command (String) 103 """ 104 result = None 105 #init redirection 106 if silent: 107 logfd = open(os.path.devnull, "r+") 108 else: 109 logfd = tmpfile() 110 logname = logfd.name 111 gdb.execute('set logging off') # prevent nested call 112 gdb.execute('set height 0') # disable paging 113 gdb.execute('set logging file %s' % logname) 114 gdb.execute('set logging overwrite on') 115 gdb.execute('set logging redirect on') 116 gdb.execute('set logging on') 117 try: 118 gdb.execute(gdb_command) 119 gdb.flush() 120 gdb.execute('set logging off') 121 if not silent: 122 logfd.flush() 123 result = logfd.read() 124 logfd.close() 125 except Exception as e: 126 gdb.execute('set logging off') #to be sure 127 if config.Option.get("debug") == "on": 128 msg('Exception (%s): %s' % (gdb_command, e), "red") 129 traceback.print_exc() 130 logfd.close() 131 if config.Option.get("verbose") == "on": 132 msg(result) 133 return result 134 135 def parse_and_eval(self, exp): 136 """ 137 Work around implementation for gdb.parse_and_eval with enhancements 138 139 Args: 140 - exp: expression to evaluate (String) 141 142 Returns: 143 - value of expression 144 """ 145 146 regs = sum(REGISTERS.values(), []) 147 for r in regs: 148 if "$"+r not in exp and "e"+r not in exp and "r"+r not in exp: 149 exp = exp.replace(r, "$%s" % r) 150 151 p = re.compile("(.*)\[(.*)\]") # DWORD PTR [esi+eax*1] 152 matches = p.search(exp) 153 if not matches: 154 p = re.compile("(.*).s:(0x.*)") # DWORD PTR ds:0xdeadbeef 155 matches = p.search(exp) 156 157 if matches: 158 mod = "w" 159 if "BYTE" in matches.group(1): 160 mod = "b" 161 elif "QWORD" in matches.group(1): 162 mod = "g" 163 elif "DWORD" in matches.group(1): 164 mod = "w" 165 elif "WORD" in matches.group(1): 166 mod = "h" 167 168 out = self.execute_redirect("x/%sx %s" % (mod, matches.group(2))) 169 if not out: 170 return None 171 else: 172 return out.split(":\t")[-1].strip() 173 174 else: 175 out = self.execute_redirect("print %s" % exp) 176 if not out: 177 return None 178 else: 179 out = gdb.history(0).__str__() 180 out = out.encode('ascii', 'ignore') 181 out = decode_string_escape(out) 182 return out.strip() 183 184 def string_to_argv(self, str): 185 """ 186 Convert a string to argv list, pre-processing register and variable values 187 188 Args: 189 - str: input string (String) 190 191 Returns: 192 - argv list (List) 193 """ 194 try: 195 str = str.encode('ascii', 'ignore') 196 except: 197 pass 198 args = list(map(lambda x: decode_string_escape(x), shlex.split(str.decode()))) 199 # need more processing here 200 for idx, a in enumerate(args): 201 a = a.strip(",") 202 if a.startswith("$"): # try to get register/variable value 203 v = self.parse_and_eval(a) 204 if v != None and v != "void": 205 if v.startswith("0x"): # int 206 args[idx] = v.split()[0] # workaround for 0xdeadbeef <symbol+x> 207 else: # string, complex data 208 args[idx] = v 209 elif a.startswith("+"): # relative value to prev arg 210 adder = to_int(self.parse_and_eval(a[1:])) 211 if adder is not None: 212 args[idx] = "%s" % to_hex(to_int(args[idx-1]) + adder) 213 elif is_math_exp(a): 214 try: 215 v = eval("%s" % a) 216 # XXX hack to avoid builtin functions/types 217 if not isinstance(v, six.string_types + six.integer_types): 218 continue 219 args[idx] = "%s" % (to_hex(v) if to_int(v) != None else v) 220 except: 221 pass 222 if config.Option.get("verbose") == "on": 223 msg(args) 224 return args 225 226 227 ################################ 228 # GDB User-Defined Helpers # 229 ################################ 230 def save_user_command(self, cmd): 231 """ 232 Save user-defined command and deactivate it 233 234 Args: 235 - cmd: user-defined command (String) 236 237 Returns: 238 - True if success to save (Bool) 239 """ 240 commands = self.execute_redirect("show user %s" % cmd) 241 if not commands: 242 return False 243 244 commands = "\n".join(commands.splitlines()[1:]) 245 commands = "define %s\n" % cmd + commands + "end\n" 246 self.SAVED_COMMANDS[cmd] = commands 247 tmp = tmpfile() 248 tmp.write("define %s\nend\n" % cmd) 249 tmp.flush() 250 result = self.execute("source %s" % tmp.name) 251 tmp.close() 252 return result 253 254 def define_user_command(self, cmd, code): 255 """ 256 Define a user-defined command, overwrite the old content 257 258 Args: 259 - cmd: user-defined command (String) 260 - code: gdb script code to append (String) 261 262 Returns: 263 - True if success to define (Bool) 264 """ 265 commands = "define %s\n" % cmd + code + "\nend\n" 266 tmp = tmpfile(is_binary_file=False) 267 tmp.write(commands) 268 tmp.flush() 269 result = self.execute("source %s" % tmp.name) 270 tmp.close() 271 return result 272 273 def append_user_command(self, cmd, code): 274 """ 275 Append code to a user-defined command, define new command if not exist 276 277 Args: 278 - cmd: user-defined command (String) 279 - code: gdb script code to append (String) 280 281 Returns: 282 - True if success to append (Bool) 283 """ 284 285 commands = self.execute_redirect("show user %s" % cmd) 286 if not commands: 287 return self.define_user_command(cmd, code) 288 # else 289 commands = "\n".join(commands.splitlines()[1:]) 290 if code in commands: 291 return True 292 293 commands = "define %s\n" % cmd + commands + code + "\nend\n" 294 tmp = tmpfile() 295 tmp.write(commands) 296 tmp.flush() 297 result = self.execute("source %s" % tmp.name) 298 tmp.close() 299 return result 300 301 def restore_user_command(self, cmd): 302 """ 303 Restore saved user-defined command 304 305 Args: 306 - cmd: user-defined command (String) 307 308 Returns: 309 - True if success to restore (Bool) 310 """ 311 if cmd == "all": 312 commands = "\n".join(self.SAVED_COMMANDS.values()) 313 self.SAVED_COMMANDS = {} 314 else: 315 if cmd not in self.SAVED_COMMANDS: 316 return False 317 else: 318 commands = self.SAVED_COMMANDS[cmd] 319 self.SAVED_COMMANDS.pop(cmd) 320 tmp = tmpfile() 321 tmp.write(commands) 322 tmp.flush() 323 result = self.execute("source %s" % tmp.name) 324 tmp.close() 325 326 return result 327 328 def run_gdbscript_code(self, code): 329 """ 330 Run basic gdbscript code as it is typed in interactively 331 332 Args: 333 - code: gdbscript code, lines are splitted by "\n" or ";" (String) 334 335 Returns: 336 - True if success to run (Bool) 337 """ 338 tmp = tmpfile() 339 tmp.write(code.replace(";", "\n")) 340 tmp.flush() 341 result = self.execute("source %s" % tmp.name) 342 tmp.close() 343 return result 344 345 ######################### 346 # Debugging Helpers # 347 ######################### 348 @memoized 349 def is_target_remote(self): 350 """ 351 Check if current target is remote 352 353 Returns: 354 - True if target is remote (Bool) 355 """ 356 out = self.execute_redirect("info program") 357 if out and "serial line" in out: # remote target 358 return True 359 360 return False 361 362 @memoized 363 def getfile(self): 364 """ 365 Get exec file of debugged program 366 367 Returns: 368 - full path to executable file (String) 369 """ 370 result = None 371 out = self.execute_redirect('info files') 372 if out and '"' in out: 373 p = re.compile(".*exec file:\s*`(.*)'") 374 m = p.search(out) 375 if m: 376 result = m.group(1) 377 else: # stripped file, get symbol file 378 p = re.compile("Symbols from \"([^\"]*)") 379 m = p.search(out) 380 if m: 381 result = m.group(1) 382 383 return result 384 385 def get_status(self): 386 """ 387 Get execution status of debugged program 388 389 Returns: 390 - current status of program (String) 391 STOPPED - not being run 392 BREAKPOINT - breakpoint hit 393 SIGXXX - stopped by signal XXX 394 UNKNOWN - unknown, not implemented 395 """ 396 status = "UNKNOWN" 397 out = self.execute_redirect("info program") 398 for line in out.splitlines(): 399 if line.startswith("It stopped"): 400 if "signal" in line: # stopped by signal 401 status = line.split("signal")[1].split(",")[0].strip() 402 break 403 if "breakpoint" in line: # breakpoint hit 404 status = "BREAKPOINT" 405 break 406 if "not being run" in line: 407 status = "STOPPED" 408 break 409 return status 410 411 @memoized 412 def getpid(self): 413 """ 414 Get PID of the debugged process 415 416 Returns: 417 - pid (Int) 418 """ 419 420 out = None 421 status = self.get_status() 422 if not status or status == "STOPPED": 423 return None 424 pid = gdb.selected_inferior().pid 425 return int(pid) if pid else None 426 427 def getos(self): 428 """ 429 Get running OS info 430 431 Returns: 432 - os version (String) 433 """ 434 # TODO: get remote os by calling uname() 435 return os.uname()[0] 436 437 @memoized 438 def getarch(self): 439 """ 440 Get architecture of debugged program 441 442 Returns: 443 - tuple of architecture info (arch (String), bits (Int)) 444 """ 445 arch = "unknown" 446 bits = 32 447 out = self.execute_redirect('maintenance info sections ?').splitlines() 448 for line in out: 449 if "file type" in line: 450 arch = line.split()[-1][:-1] 451 break 452 if "64" in arch: 453 bits = 64 454 return (arch, bits) 455 456 def intsize(self): 457 """ 458 Get dword size of debugged program 459 460 Returns: 461 - size (Int) 462 + intsize = 4/8 for 32/64-bits arch 463 """ 464 465 (arch, bits) = self.getarch() 466 return bits // 8 467 468 def getregs(self, reglist=None): 469 """ 470 Get value of some or all registers 471 472 Returns: 473 - dictionary of {regname(String) : value(Int)} 474 """ 475 if reglist: 476 reglist = reglist.replace(",", " ") 477 else: 478 reglist = "" 479 regs = self.execute_redirect("info registers %s" % reglist) 480 if not regs: 481 return None 482 483 result = {} 484 if regs: 485 for r in regs.splitlines(): 486 r = r.split() 487 if len(r) > 1 and to_int(r[1]) is not None: 488 result[r[0]] = to_int(r[1]) 489 490 return result 491 492 def getreg(self, register): 493 """ 494 Get value of a specific register 495 496 Args: 497 - register: register name (String) 498 499 Returns: 500 - register value (Int) 501 """ 502 r = register.lower() 503 regs = self.execute_redirect("info registers %s" % r) 504 if regs: 505 regs = regs.splitlines() 506 if len(regs) > 1: 507 return None 508 else: 509 result = to_int(regs[0].split()[1]) 510 return result 511 512 return None 513 514 def set_breakpoint(self, location, temp=0, hard=0): 515 """ 516 Wrapper for GDB break command 517 - location: target function or address (String ot Int) 518 519 Returns: 520 - True if can set breakpoint 521 """ 522 cmd = "break" 523 if hard: 524 cmd = "h" + cmd 525 if temp: 526 cmd = "t" + cmd 527 528 if to_int(location) is not None: 529 return peda.execute("%s *0x%x" % (cmd, to_int(location))) 530 else: 531 return peda.execute("%s %s" % (cmd, location)) 532 533 def get_breakpoint(self, num): 534 """ 535 Get info of a specific breakpoint 536 TODO: support catchpoint, watchpoint 537 538 Args: 539 - num: breakpoint number 540 541 Returns: 542 - tuple (Num(Int), Type(String), Disp(Bool), Enb(Bool), Address(Int), What(String), commands(String)) 543 """ 544 out = self.execute_redirect("info breakpoints %d" % num) 545 if not out or "No breakpoint" in out: 546 return None 547 548 lines = out.splitlines()[1:] 549 # breakpoint regex 550 p = re.compile("^(\d*)\s*(.*breakpoint)\s*(keep|del)\s*(y|n)\s*(0x[^ ]*)\s*(.*)") 551 m = p.match(lines[0]) 552 if not m: 553 # catchpoint/watchpoint regex 554 p = re.compile("^(\d*)\s*(.*point)\s*(keep|del)\s*(y|n)\s*(.*)") 555 m = p.match(lines[0]) 556 if not m: 557 return None 558 else: 559 (num, type, disp, enb, what) = m.groups() 560 addr = '' 561 else: 562 (num, type, disp, enb, addr, what) = m.groups() 563 564 disp = True if disp == "keep" else False 565 enb = True if enb == "y" else False 566 addr = to_int(addr) 567 m = re.match("in.*at(.*:\d*)", what) 568 if m: 569 what = m.group(1) 570 else: 571 if addr: # breakpoint 572 what = "" 573 574 commands = "" 575 if len(lines) > 1: 576 for line in lines[1:]: 577 if "already hit" in line: continue 578 commands += line + "\n" 579 580 return (num, type, disp, enb, addr, what, commands.rstrip()) 581 582 def get_breakpoints(self): 583 """ 584 Get list of current breakpoints 585 586 Returns: 587 - list of tuple (Num(Int), Type(String), Disp(Bool), Nnb(Bool), Address(Int), commands(String)) 588 """ 589 result = [] 590 out = self.execute_redirect("info breakpoints") 591 if not out: 592 return [] 593 594 bplist = [] 595 for line in out.splitlines(): 596 m = re.match("^(\d*).*", line) 597 if m and to_int(m.group(1)): 598 bplist += [to_int(m.group(1))] 599 600 for num in bplist: 601 r = self.get_breakpoint(num) 602 if r: 603 result += [r] 604 return result 605 606 def save_breakpoints(self, filename): 607 """ 608 Save current breakpoints to file as a script 609 610 Args: 611 - filename: target file (String) 612 613 Returns: 614 - True if success to save (Bool) 615 """ 616 # use built-in command for gdb 7.2+ 617 result = self.execute_redirect("save breakpoints %s" % filename) 618 if result == '': 619 return True 620 621 bplist = self.get_breakpoints() 622 if not bplist: 623 return False 624 625 try: 626 fd = open(filename, "w") 627 for (num, type, disp, enb, addr, what, commands) in bplist: 628 m = re.match("(.*)point", type) 629 if m: 630 cmd = m.group(1).split()[-1] 631 else: 632 cmd = "break" 633 if "hw" in type and cmd == "break": 634 cmd = "h" + cmd 635 if "read" in type: 636 cmd = "r" + cmd 637 if "acc" in type: 638 cmd = "a" + cmd 639 640 if not disp: 641 cmd = "t" + cmd 642 if what: 643 location = what 644 else: 645 location = "*0x%x" % addr 646 text = "%s %s" % (cmd, location) 647 if commands: 648 if "stop only" not in commands: 649 text += "\ncommands\n%s\nend" % commands 650 else: 651 text += commands.split("stop only", 1)[1] 652 fd.write(text + "\n") 653 fd.close() 654 return True 655 except: 656 return False 657 658 def get_config_filename(self, name): 659 filename = peda.getfile() 660 if not filename: 661 filename = peda.getpid() 662 if not filename: 663 filename = 'unknown' 664 665 filename = os.path.basename("%s" % filename) 666 tmpl_name = config.Option.get(name) 667 if tmpl_name: 668 return tmpl_name.replace("#FILENAME#", filename) 669 else: 670 return "peda-%s-%s" % (name, filename) 671 672 def save_session(self, filename=None): 673 """ 674 Save current working gdb session to file as a script 675 676 Args: 677 - filename: target file (String) 678 679 Returns: 680 - True if success to save (Bool) 681 """ 682 session = "" 683 if not filename: 684 filename = self.get_config_filename("session") 685 686 # exec-wrapper 687 out = self.execute_redirect("show exec-wrapper") 688 wrapper = out.split('"')[1] 689 if wrapper: 690 session += "set exec-wrapper %s\n" % wrapper 691 692 try: 693 # save breakpoints 694 self.save_breakpoints(filename) 695 fd = open(filename, "a+") 696 fd.write("\n" + session) 697 fd.close() 698 return True 699 except: 700 return False 701 702 def restore_session(self, filename=None): 703 """ 704 Restore previous saved working gdb session from file 705 706 Args: 707 - filename: source file (String) 708 709 Returns: 710 - True if success to restore (Bool) 711 """ 712 if not filename: 713 filename = self.get_config_filename("session") 714 715 # temporarily save and clear breakpoints 716 tmp = tmpfile() 717 self.save_breakpoints(tmp.name) 718 self.execute("delete") 719 result = self.execute("source %s" % filename) 720 if not result: 721 self.execute("source %s" % tmp.name) 722 tmp.close() 723 return result 724 725 @memoized 726 def assemble(self, asmcode, bits=None): 727 """ 728 Assemble ASM instructions using NASM 729 - asmcode: input ASM instructions, multiple instructions are separated by ";" (String) 730 731 Returns: 732 - bin code (raw bytes) 733 """ 734 if bits is None: 735 (arch, bits) = self.getarch() 736 return Nasm.assemble(asmcode, bits) 737 738 def disassemble(self, *arg): 739 """ 740 Wrapper for disassemble command 741 - arg: args for disassemble command 742 743 Returns: 744 - text code (String) 745 """ 746 code = "" 747 modif = "" 748 arg = list(arg) 749 if len(arg) > 1: 750 if "/" in arg[0]: 751 modif = arg[0] 752 arg = arg[1:] 753 if len(arg) == 1 and to_int(arg[0]) != None: 754 arg += [to_hex(to_int(arg[0]) + 32)] 755 756 self.execute("set disassembly-flavor intel") 757 out = self.execute_redirect("disassemble %s %s" % (modif, ",".join(arg))) 758 if not out: 759 return None 760 else: 761 code = out 762 763 return code 764 765 @memoized 766 def prev_inst(self, address, count=1): 767 """ 768 Get previous instructions at an address 769 770 Args: 771 - address: address to get previous instruction (Int) 772 - count: number of instructions to read (Int) 773 774 Returns: 775 - list of tuple (address(Int), code(String)) 776 """ 777 result = [] 778 backward = 64+16*count 779 for i in range(backward): 780 if self.getpid() and not self.is_address(address-backward+i): 781 continue 782 783 code = self.execute_redirect("disassemble %s, %s" % (to_hex(address-backward+i), to_hex(address+1))) 784 if code and ("%x" % address) in code: 785 lines = code.strip().splitlines()[1:-1] 786 if len(lines) > count and "(bad)" not in " ".join(lines): 787 for line in lines[-count-1:-1]: 788 (addr, code) = line.split(":", 1) 789 addr = re.search("(0x[^ ]*)", addr).group(1) 790 result += [(to_int(addr), code)] 791 return result 792 return None 793 794 @memoized 795 def current_inst(self, address): 796 """ 797 Parse instruction at an address 798 799 Args: 800 - address: address to get next instruction (Int) 801 802 Returns: 803 - tuple of (address(Int), code(String)) 804 """ 805 out = self.execute_redirect("x/i 0x%x" % address) 806 if not out: 807 return None 808 809 (addr, code) = out.split(":", 1) 810 addr = re.search("(0x[^ ]*)", addr).group(1) 811 addr = to_int(addr) 812 code = code.strip() 813 814 return (addr, code) 815 816 @memoized 817 def next_inst(self, address, count=1): 818 """ 819 Get next instructions at an address 820 821 Args: 822 - address: address to get next instruction (Int) 823 - count: number of instructions to read (Int) 824 825 Returns: 826 - - list of tuple (address(Int), code(String)) 827 """ 828 result = [] 829 code = self.execute_redirect("x/%di 0x%x" % (count+1, address)) 830 if not code: 831 return None 832 833 lines = code.strip().splitlines() 834 for i in range(1, count+1): 835 (addr, code) = lines[i].split(":", 1) 836 addr = re.search("(0x[^ ]*)", addr).group(1) 837 result += [(to_int(addr), code)] 838 return result 839 840 @memoized 841 def disassemble_around(self, address, count=8): 842 """ 843 Disassemble instructions nearby current PC or an address 844 845 Args: 846 - address: start address to disassemble around (Int) 847 - count: number of instructions to disassemble 848 849 Returns: 850 - text code (String) 851 """ 852 count = min(count, 256) 853 pc = address 854 if pc is None: 855 return None 856 857 # check if address is reachable 858 if not self.execute_redirect("x/x 0x%x" % pc): 859 return None 860 861 prev_code = self.prev_inst(pc, count//2-1) 862 if prev_code: 863 start = prev_code[0][0] 864 else: 865 start = pc 866 if start == pc: 867 count = count//2 868 869 code = self.execute_redirect("x/%di 0x%x" % (count, start)) 870 if "0x%x" % pc not in code: 871 code = self.execute_redirect("x/%di 0x%x" % (count//2, pc)) 872 873 return code.rstrip() 874 875 @memoized 876 def xrefs(self, search="", filename=None): 877 """ 878 Search for all call references or data access to a function/variable 879 880 Args: 881 - search: function or variable to search for (String) 882 - filename: binary/library to search (String) 883 884 Returns: 885 - list of tuple (address(Int), asm instruction(String)) 886 """ 887 result = [] 888 if not filename: 889 filename = self.getfile() 890 891 if not filename: 892 return None 893 vmap = self.get_vmmap(filename) 894 elfbase = vmap[0][0] if vmap else 0 895 896 if to_int(search) is not None: 897 search = "%x" % to_int(search) 898 899 search_data = 1 900 if search == "": 901 search_data = 0 902 903 out = execute_external_command("%s -M intel -z --prefix-address -d '%s' | grep '%s'" % (config.OBJDUMP, filename, search)) 904 905 for line in out.splitlines(): 906 if not line: continue 907 addr = to_int("0x" + line.split()[0].strip()) 908 if not addr: continue 909 910 # update with runtime values 911 if addr < elfbase: 912 addr += elfbase 913 out = self.execute_redirect("x/i 0x%x" % addr) 914 if out: 915 line = out 916 p = re.compile("\s*(0x[^ ]*).*?:\s*([^ ]*)\s*(.*)") 917 else: 918 p = re.compile("(.*?)\s*<.*?>\s*([^ ]*)\s*(.*)") 919 920 m = p.search(line) 921 if m: 922 (address, opcode, opers) = m.groups() 923 if "call" in opcode and search in opers: 924 result += [(addr, line.strip())] 925 if search_data: 926 if "mov" in opcode and search in opers: 927 result += [(addr, line.strip())] 928 929 return result 930 931 def _get_function_args_32(self, code, argc=None): 932 """ 933 Guess the number of arguments passed to a function - i386 934 """ 935 if not argc: 936 argc = 0 937 p = re.compile(".*mov.*\[esp(.*)\],") 938 matches = p.findall(code) 939 if matches: 940 l = len(matches) 941 for v in matches: 942 if v.startswith("+"): 943 offset = to_int(v[1:]) 944 if offset is not None and (offset//4) > l: 945 continue 946 argc += 1 947 else: # try with push style 948 argc = code.count("push") 949 950 argc = min(argc, 6) 951 if argc == 0: 952 return [] 953 954 args = [] 955 sp = self.getreg("sp") 956 mem = self.dumpmem(sp, sp+4*argc) 957 for i in range(argc): 958 args += [struct.unpack("<L", mem[i*4:(i+1)*4])[0]] 959 960 return args 961 962 def _get_function_args_64(self, code, argc=None): 963 """ 964 Guess the number of arguments passed to a function - x86_64 965 """ 966 967 # just retrieve max 6 args 968 arg_order = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"] 969 p = re.compile(":\s*([^ ]*)\s*(.*),") 970 matches = p.findall(code) 971 regs = [r for (_, r) in matches] 972 p = re.compile(("di|si|dx|cx|r8|r9")) 973 m = p.findall(" ".join(regs)) 974 m = list(set(m)) # uniqify 975 argc = 0 976 if "si" in m and "di" not in m: # dirty fix 977 argc += 1 978 argc += m.count("di") 979 if argc > 0: 980 argc += m.count("si") 981 if argc > 1: 982 argc += m.count("dx") 983 if argc > 2: 984 argc += m.count("cx") 985 if argc > 3: 986 argc += m.count("r8") 987 if argc > 4: 988 argc += m.count("r9") 989 990 if argc == 0: 991 return [] 992 993 args = [] 994 regs = self.getregs() 995 for i in range(argc): 996 args += [regs[arg_order[i]]] 997 998 return args 999 1000 def get_function_args(self, argc=None): 1001 """ 1002 Get the guessed arguments passed to a function when stopped at a call instruction 1003 1004 Args: 1005 - argc: force to get specific number of arguments (Int) 1006 1007 Returns: 1008 - list of arguments (List) 1009 """ 1010 1011 args = [] 1012 regs = self.getregs() 1013 if regs is None: 1014 return [] 1015 1016 (arch, bits) = self.getarch() 1017 pc = self.getreg("pc") 1018 prev_insts = self.prev_inst(pc, 12) 1019 1020 code = "" 1021 if not prev_insts: 1022 return [] 1023 1024 for (addr, inst) in prev_insts[::-1]: 1025 if "call" in inst.strip().split()[0]: 1026 break 1027 code = "0x%x:%s\n" % (addr, inst) + code 1028 1029 if "i386" in arch: 1030 args = self._get_function_args_32(code, argc) 1031 if "64" in arch: 1032 args = self._get_function_args_64(code, argc) 1033 1034 return args 1035 1036 @memoized 1037 def backtrace_depth(self, sp=None): 1038 """ 1039 Get number of frames in backtrace 1040 1041 Args: 1042 - sp: stack pointer address, for caching (Int) 1043 1044 Returns: 1045 - depth: number of frames (Int) 1046 """ 1047 backtrace = self.execute_redirect("backtrace") 1048 return backtrace.count("#") 1049 1050 def stepuntil(self, inst, mapname=None, depth=None): 1051 """ 1052 Step execution until next "inst" instruction within a specific memory range 1053 1054 Args: 1055 - inst: the instruction to reach (String) 1056 - mapname: name of virtual memory region to check for the instruction (String) 1057 - depth: backtrace depth (Int) 1058 1059 Returns: 1060 - tuple of (depth, instruction) 1061 + depth: current backtrace depth (Int) 1062 + instruction: current instruction (String) 1063 """ 1064 1065 if not self.getpid(): 1066 return None 1067 1068 maxdepth = to_int(config.Option.get("tracedepth")) 1069 if not maxdepth: 1070 maxdepth = 0xffffffff 1071 1072 maps = self.get_vmmap() 1073 binname = self.getfile() 1074 if mapname is None: 1075 mapname = binname 1076 mapname = mapname.replace(" ", "").split(",") + [binname] 1077 targetmap = [] 1078 for m in mapname: 1079 targetmap += self.get_vmmap(m) 1080 binmap = self.get_vmmap("binary") 1081 1082 current_instruction = "" 1083 pc = self.getreg("pc") 1084 1085 if depth is None: 1086 current_depth = self.backtrace_depth(self.getreg("sp")) 1087 else: 1088 current_depth = depth 1089 old_status = self.get_status() 1090 1091 while True: 1092 status = self.get_status() 1093 if status != old_status: 1094 if "SIG" in status and status[3:] not in ["TRAP"] and not to_int(status[3:]): # ignore TRAP and numbered signals 1095 current_instruction = "Interrupted: %s" % status 1096 call_depth = current_depth 1097 break 1098 if "STOP" in status: 1099 current_instruction = "End of execution" 1100 call_depth = current_depth 1101 break 1102 1103 call_depth = self.backtrace_depth(self.getreg("sp")) 1104 current_instruction = self.execute_redirect("x/i $pc") 1105 if not current_instruction: 1106 current_instruction = "End of execution" 1107 break 1108 1109 p = re.compile(".*?(0x[^ :]*)") 1110 addr = p.search(current_instruction).group(1) 1111 addr = to_int(addr) 1112 if addr is None: 1113 break 1114 1115 #p = re.compile(".*?:\s*([^ ]*)") 1116 p = re.compile(".*?:\s*(.*)") 1117 code = p.match(current_instruction).group(1) 1118 found = 0 1119 for i in inst.replace(",", " ").split(): 1120 if re.match(i.strip(), code.strip()): 1121 if self.is_address(addr, targetmap) and addr != pc: 1122 found = 1 1123 break 1124 if found != 0: 1125 break 1126 self.execute_redirect("stepi", silent=True) 1127 if not self.is_address(addr, targetmap) or call_depth > maxdepth: 1128 self.execute_redirect("finish", silent=True) 1129 pc = 0 1130 1131 return (call_depth - current_depth, current_instruction.strip()) 1132 1133 def get_eflags(self): 1134 """ 1135 Get flags value from EFLAGS register 1136 1137 Returns: 1138 - dictionary of named flags 1139 """ 1140 1141 # Eflags bit masks, source vdb 1142 EFLAGS_CF = 1 << 0 1143 EFLAGS_PF = 1 << 2 1144 EFLAGS_AF = 1 << 4 1145 EFLAGS_ZF = 1 << 6 1146 EFLAGS_SF = 1 << 7 1147 EFLAGS_TF = 1 << 8 1148 EFLAGS_IF = 1 << 9 1149 EFLAGS_DF = 1 << 10 1150 EFLAGS_OF = 1 << 11 1151 1152 flags = {"CF":0, "PF":0, "AF":0, "ZF":0, "SF":0, "TF":0, "IF":0, "DF":0, "OF":0} 1153 eflags = self.getreg("eflags") 1154 if not eflags: 1155 return None 1156 flags["CF"] = bool(eflags & EFLAGS_CF) 1157 flags["PF"] = bool(eflags & EFLAGS_PF) 1158 flags["AF"] = bool(eflags & EFLAGS_AF) 1159 flags["ZF"] = bool(eflags & EFLAGS_ZF) 1160 flags["SF"] = bool(eflags & EFLAGS_SF) 1161 flags["TF"] = bool(eflags & EFLAGS_TF) 1162 flags["IF"] = bool(eflags & EFLAGS_IF) 1163 flags["DF"] = bool(eflags & EFLAGS_DF) 1164 flags["OF"] = bool(eflags & EFLAGS_OF) 1165 1166 return flags 1167 1168 def set_eflags(self, flagname, value): 1169 """ 1170 Set/clear/toggle value of a flag register 1171 1172 Returns: 1173 - True if success (Bool) 1174 """ 1175 1176 # Eflags bit masks, source vdb 1177 EFLAGS_CF = 1 << 0 1178 EFLAGS_PF = 1 << 2 1179 EFLAGS_AF = 1 << 4 1180 EFLAGS_ZF = 1 << 6 1181 EFLAGS_SF = 1 << 7 1182 EFLAGS_TF = 1 << 8 1183 EFLAGS_IF = 1 << 9 1184 EFLAGS_DF = 1 << 10 1185 EFLAGS_OF = 1 << 11 1186 1187 flags = {"carry": "CF", "parity": "PF", "adjust": "AF", "zero": "ZF", "sign": "SF", 1188 "trap": "TF", "interrupt": "IF", "direction": "DF", "overflow": "OF"} 1189 1190 flagname = flagname.lower() 1191 1192 if flagname not in flags: 1193 return False 1194 1195 eflags = self.get_eflags() 1196 if not eflags: 1197 return False 1198 1199 # If value doesn't match the current, or we want to toggle, toggle 1200 if value is None or eflags[flags[flagname]] != value: 1201 reg_eflags = self.getreg("eflags") 1202 reg_eflags ^= eval("EFLAGS_%s" % flags[flagname]) 1203 result = self.execute("set $eflags = 0x%x" % reg_eflags) 1204 return result 1205 1206 return True 1207 1208 def eval_target(self, inst): 1209 """ 1210 Evaluate target address of an instruction, used for jumpto decision 1211 1212 Args: 1213 - inst: ASM instruction text (String) 1214 1215 Returns: 1216 - target address (Int) 1217 """ 1218 1219 target = None 1220 inst = inst.strip() 1221 opcode = inst.split(":\t")[-1].split()[0] 1222 # this regex includes x86_64 RIP relateive address reference 1223 p = re.compile(".*?:\s*[^ ]*\s*(.* PTR ).*(0x[^ ]*)") 1224 m = p.search(inst) 1225 if not m: 1226 p = re.compile(".*?:\s.*\s(0x[^ ]*|\w+)") 1227 m = p.search(inst) 1228 if m: 1229 target = m.group(1) 1230 target = self.parse_and_eval(target) 1231 else: 1232 target = None 1233 else: 1234 if "]" in m.group(2): # e.g DWORD PTR [ebx+0xc] 1235 p = re.compile(".*?:\s*[^ ]*\s*(.* PTR ).*\[(.*)\]") 1236 m = p.search(inst) 1237 target = self.parse_and_eval("%s[%s]" % (m.group(1), m.group(2).strip())) 1238 1239 return to_int(target) 1240 1241 def testjump(self, inst=None): 1242 """ 1243 Test if jump instruction is taken or not 1244 1245 Returns: 1246 - (status, address of target jumped instruction) 1247 """ 1248 1249 flags = self.get_eflags() 1250 if not flags: 1251 return None 1252 1253 if not inst: 1254 pc = self.getreg("pc") 1255 inst = self.execute_redirect("x/i 0x%x" % pc) 1256 if not inst: 1257 return None 1258 1259 opcode = inst.split(":\t")[-1].split()[0] 1260 next_addr = self.eval_target(inst) 1261 if next_addr is None: 1262 next_addr = 0 1263 1264 if opcode == "jmp": 1265 return next_addr 1266 if opcode == "je" and flags["ZF"]: 1267 return next_addr 1268 if opcode == "jne" and not flags["ZF"]: 1269 return next_addr 1270 if opcode == "jg" and not flags["ZF"] and (flags["SF"] == flags["OF"]): 1271 return next_addr 1272 if opcode == "jge" and (flags["SF"] == flags["OF"]): 1273 return next_addr 1274 if opcode == "ja" and not flags["CF"] and not flags["ZF"]: 1275 return next_addr 1276 if opcode == "jae" and not flags["CF"]: 1277 return next_addr 1278 if opcode == "jl" and (flags["SF"] != flags["OF"]): 1279 return next_addr 1280 if opcode == "jle" and (flags["ZF"] or (flags["SF"] != flags["OF"])): 1281 return next_addr 1282 if opcode == "jb" and flags["CF"]: 1283 return next_addr 1284 if opcode == "jbe" and (flags["CF"] or flags["ZF"]): 1285 return next_addr 1286 if opcode == "jo" and flags["OF"]: 1287 return next_addr 1288 if opcode == "jno" and not flags["OF"]: 1289 return next_addr 1290 if opcode == "jz" and flags["ZF"]: 1291 return next_addr 1292 if opcode == "jnz" and flags["OF"]: 1293 return next_addr 1294 1295 return None 1296 1297 def take_snapshot(self): 1298 """ 1299 Take a snapshot of current process 1300 Warning: this is not thread safe, do not use with multithread program 1301 1302 Returns: 1303 - dictionary of snapshot data 1304 """ 1305 if not self.getpid(): 1306 return None 1307 1308 maps = self.get_vmmap() 1309 if not maps: 1310 return None 1311 1312 snapshot = {} 1313 # get registers 1314 snapshot["reg"] = self.getregs() 1315 # get writable memory regions 1316 snapshot["mem"] = {} 1317 for (start, end, perm, _) in maps: 1318 if "w" in perm: 1319 snapshot["mem"][start] = self.dumpmem(start, end) 1320 1321 return snapshot 1322 1323 def save_snapshot(self, filename=None): 1324 """ 1325 Save a snapshot of current process to file 1326 Warning: this is not thread safe, do not use with multithread program 1327 1328 Args: 1329 - filename: target file to save snapshot 1330 1331 Returns: 1332 - Bool 1333 """ 1334 if not filename: 1335 filename = self.get_config_filename("snapshot") 1336 1337 snapshot = self.take_snapshot() 1338 if not snapshot: 1339 return False 1340 # dump to file 1341 fd = open(filename, "wb") 1342 pickle.dump(snapshot, fd, pickle.HIGHEST_PROTOCOL) 1343 fd.close() 1344 1345 return True 1346 1347 def give_snapshot(self, snapshot): 1348 """ 1349 Restore a saved snapshot of current process 1350 Warning: this is not thread safe, do not use with multithread program 1351 1352 Returns: 1353 - Bool 1354 """ 1355 if not snapshot or not self.getpid(): 1356 return False 1357 1358 # restore memory regions 1359 for (addr, buf) in snapshot["mem"].items(): 1360 self.writemem(addr, buf) 1361 1362 # restore registers, SP will be the last one 1363 for (r, v) in snapshot["reg"].items(): 1364 self.execute("set $%s = 0x%x" % (r, v)) 1365 if r.endswith("sp"): 1366 sp = v 1367 self.execute("set $sp = 0x%x" % sp) 1368 1369 return True 1370 1371 def restore_snapshot(self, filename=None): 1372 """ 1373 Restore a saved snapshot of current process from file 1374 Warning: this is not thread safe, do not use with multithread program 1375 1376 Args: 1377 - file: saved snapshot 1378 1379 Returns: 1380 - Bool 1381 """ 1382 if not filename: 1383 filename = self.get_config_filename("snapshot") 1384 1385 fd = open(filename, "rb") 1386 snapshot = pickle.load(fd) 1387 return self.give_snapshot(snapshot) 1388 1389 1390 ######################### 1391 # Memory Operations # 1392 ######################### 1393 @memoized 1394 def get_vmmap(self, name=None): 1395 """ 1396 Get virtual memory mapping address ranges of debugged process 1397 1398 Args: 1399 - name: name/address of binary/library to get mapping range (String) 1400 + name = "binary" means debugged program 1401 + name = "all" means all virtual maps 1402 1403 Returns: 1404 - list of virtual mapping ranges (start(Int), end(Int), permission(String), mapname(String)) 1405 1406 """ 1407 def _get_offline_maps(): 1408 name = self.getfile() 1409 if not name: 1410 return None 1411 headers = self.elfheader() 1412 binmap = [] 1413 hlist = [x for x in headers.items() if x[1][2] == 'code'] 1414 hlist = sorted(hlist, key=lambda x:x[1][0]) 1415 binmap += [(hlist[0][1][0], hlist[-1][1][1], "rx-p", name)] 1416 1417 hlist = [x for x in headers.items() if x[1][2] == 'rodata'] 1418 hlist = sorted(hlist, key=lambda x:x[1][0]) 1419 binmap += [(hlist[0][1][0], hlist[-1][1][1], "r--p", name)] 1420 1421 hlist = [x for x in headers.items() if x[1][2] == 'data'] 1422 hlist = sorted(hlist, key=lambda x:x[1][0]) 1423 binmap += [(hlist[0][1][0], hlist[-1][1][1], "rw-p", name)] 1424 1425 return binmap 1426 1427 def _get_allmaps_osx(pid, remote=False): 1428 maps = [] 1429 #_DATA 00007fff77975000-00007fff77976000 [ 4K] rw-/rw- SM=COW /usr/lib/system/libremovefile.dylib 1430 pattern = re.compile("([^\n]*)\s* ([0-9a-f][^-\s]*)-([^\s]*) \[.*\]\s([^/]*).* (.*)") 1431 1432 if remote: # remote target, not yet supported 1433 return maps 1434 else: # local target 1435 try: out = execute_external_command("/usr/bin/vmmap -w %s" % self.getpid()) 1436 except: error_msg("could not read vmmap of process") 1437 1438 matches = pattern.findall(out) 1439 if matches: 1440 for (name, start, end, perm, mapname) in matches: 1441 if name.startswith("Stack"): 1442 mapname = "[stack]" 1443 start = to_int("0x%s" % start) 1444 end = to_int("0x%s" % end) 1445 if mapname == "": 1446 mapname = name.strip() 1447 maps += [(start, end, perm, mapname)] 1448 return maps 1449 1450 1451 def _get_allmaps_freebsd(pid, remote=False): 1452 maps = [] 1453 mpath = "/proc/%s/map" % pid 1454 # 0x8048000 0x8049000 1 0 0xc36afdd0 r-x 1 0 0x1000 COW NC vnode /path/to/file NCH -1 1455 pattern = re.compile("0x([0-9a-f]*) 0x([0-9a-f]*)(?: [^ ]*){3} ([rwx-]*)(?: [^ ]*){6} ([^ ]*)") 1456 1457 if remote: # remote target, not yet supported 1458 return maps 1459 else: # local target 1460 try: out = open(mpath).read() 1461 except: error_msg("could not open %s; is procfs mounted?" % mpath) 1462 1463 matches = pattern.findall(out) 1464 if matches: 1465 for (start, end, perm, mapname) in matches: 1466 if start[:2] in ["bf", "7f", "ff"] and "rw" in perm: 1467 mapname = "[stack]" 1468 start = to_int("0x%s" % start) 1469 end = to_int("0x%s" % end) 1470 if mapname == "-": 1471 if start == maps[-1][1] and maps[-1][-1][0] == "/": 1472 mapname = maps[-1][-1] 1473 else: 1474 mapname = "mapped" 1475 maps += [(start, end, perm, mapname)] 1476 return maps 1477 1478 def _get_allmaps_linux(pid, remote=False): 1479 maps = [] 1480 mpath = "/proc/%s/maps" % pid 1481 #00400000-0040b000 r-xp 00000000 08:02 538840 /path/to/file 1482 pattern = re.compile("([0-9a-f]*)-([0-9a-f]*) ([rwxps-]*)(?: [^ ]*){3} *(.*)") 1483 1484 if remote: # remote target 1485 tmp = tmpfile() 1486 self.execute("remote get %s %s" % (mpath, tmp.name)) 1487 tmp.seek(0) 1488 out = tmp.read() 1489 tmp.close() 1490 else: # local target 1491 out = open(mpath).read() 1492 1493 matches = pattern.findall(out) 1494 if matches: 1495 for (start, end, perm, mapname) in matches: 1496 start = to_int("0x%s" % start) 1497 end = to_int("0x%s" % end) 1498 if mapname == "": 1499 mapname = "mapped" 1500 maps += [(start, end, perm, mapname)] 1501 return maps 1502 1503 result = [] 1504 pid = self.getpid() 1505 if not pid: # not running, try to use elfheader() 1506 try: 1507 return _get_offline_maps() 1508 except: 1509 return [] 1510 1511 # retrieve all maps 1512 os = self.getos() 1513 rmt = self.is_target_remote() 1514 maps = [] 1515 try: 1516 if os == "FreeBSD": maps = _get_allmaps_freebsd(pid, rmt) 1517 elif os == "Linux" : maps = _get_allmaps_linux(pid, rmt) 1518 elif os == "Darwin" : maps = _get_allmaps_osx(pid, rmt) 1519 except Exception as e: 1520 if config.Option.get("debug") == "on": 1521 msg("Exception: %s" %e) 1522 traceback.print_exc() 1523 1524 # select maps matched specific name 1525 if name == "binary": 1526 name = self.getfile() 1527 if name is None or name == "all": 1528 name = "" 1529 1530 if to_int(name) is None: 1531 for (start, end, perm, mapname) in maps: 1532 if name in mapname: 1533 result += [(start, end, perm, mapname)] 1534 else: 1535 addr = to_int(name) 1536 for (start, end, perm, mapname) in maps: 1537 if start <= addr and addr < end: 1538 result += [(start, end, perm, mapname)] 1539 1540 return result 1541 1542 @memoized 1543 def get_vmrange(self, address, maps=None): 1544 """ 1545 Get virtual memory mapping range of an address 1546 1547 Args: 1548 - address: target address (Int) 1549 - maps: only find in provided maps (List) 1550 1551 Returns: 1552 - tuple of virtual memory info (start, end, perm, mapname) 1553 """ 1554 if address is None: 1555 return None 1556 if maps is None: 1557 maps = self.get_vmmap() 1558 if maps: 1559 for (start, end, perm, mapname) in maps: 1560 if start <= address and end > address: 1561 return (start, end, perm, mapname) 1562 # failed to get the vmmap 1563 else: 1564 try: 1565 gdb.selected_inferior().read_memory(address, 1) 1566 start = address & 0xfffffffffffff000 1567 end = start + 0x1000 1568 return (start, end, 'rwx', 'unknown') 1569 except: 1570 return None 1571 1572 1573 @memoized 1574 def is_executable(self, address, maps=None): 1575 """ 1576 Check if an address is executable 1577 1578 Args: 1579 - address: target address (Int) 1580 - maps: only check in provided maps (List) 1581 1582 Returns: 1583 - True if address belongs to an executable address range (Bool) 1584 """ 1585 vmrange = self.get_vmrange(address, maps) 1586 if vmrange and "x" in vmrange[2]: 1587 return True 1588 else: 1589 return False 1590 1591 @memoized 1592 def is_writable(self, address, maps=None): 1593 """ 1594 Check if an address is writable 1595 1596 Args: 1597 - address: target address (Int) 1598 - maps: only check in provided maps (List) 1599 1600 Returns: 1601 - True if address belongs to a writable address range (Bool) 1602 """ 1603 vmrange = self.get_vmrange(address, maps) 1604 if vmrange and "w" in vmrange[2]: 1605 return True 1606 else: 1607 return False 1608 1609 @memoized 1610 def is_address(self, value, maps=None): 1611 """ 1612 Check if a value is a valid address (belongs to a memory region) 1613 1614 Args: 1615 - value (Int) 1616 - maps: only check in provided maps (List) 1617 1618 Returns: 1619 - True if value belongs to an address range (Bool) 1620 """ 1621 vmrange = self.get_vmrange(value, maps) 1622 return vmrange is not None 1623 1624 @memoized 1625 def get_disasm(self, address, count=1): 1626 """ 1627 Get the ASM code of instruction at address 1628 1629 Args: 1630 - address: address to read instruction (Int) 1631 - count: number of code lines (Int) 1632 1633 Returns: 1634 - asm code (String) 1635 """ 1636 code = self.execute_redirect("x/%di 0x%x" % (count, address)) 1637 if code: 1638 return code.rstrip() 1639 else: 1640 return "" 1641 1642 def dumpmem(self, start, end): 1643 """ 1644 Dump process memory from start to end 1645 1646 Args: 1647 - start: start address (Int) 1648 - end: end address (Int) 1649 1650 Returns: 1651 - memory content (raw bytes) 1652 """ 1653 mem = None 1654 logfd = tmpfile(is_binary_file=True) 1655 logname = logfd.name 1656 out = self.execute_redirect("dump memory %s 0x%x 0x%x" % (logname, start, end)) 1657 if out is None: 1658 return None 1659 else: 1660 logfd.flush() 1661 mem = logfd.read() 1662 logfd.close() 1663 1664 return mem 1665 1666 def readmem(self, address, size): 1667 """ 1668 Read content of memory at an address 1669 1670 Args: 1671 - address: start address to read (Int) 1672 - size: bytes to read (Int) 1673 1674 Returns: 1675 - memory content (raw bytes) 1676 """ 1677 # try fast dumpmem if it works 1678 mem = self.dumpmem(address, address+size) 1679 if mem is not None: 1680 return mem 1681 1682 # failed to dump, use slow x/gx way 1683 mem = "" 1684 out = self.execute_redirect("x/%dbx 0x%x" % (size, address)) 1685 if out: 1686 for line in out.splitlines(): 1687 bytes = line.split(":\t")[-1].split() 1688 mem += "".join([chr(int(c, 0)) for c in bytes]) 1689 1690 return mem 1691 1692 def read_int(self, address, intsize=None): 1693 """ 1694 Read an interger value from memory 1695 1696 Args: 1697 - address: address to read (Int) 1698 - intsize: force read size (Int) 1699 1700 Returns: 1701 - mem value (Int) 1702 """ 1703 if not intsize: 1704 intsize = self.intsize() 1705 value = self.readmem(address, intsize) 1706 if value: 1707 value = to_int("0x" + codecs.encode(value[::-1], 'hex')) 1708 return value 1709 else: 1710 return None 1711 1712 1713 def read_long(self, address): 1714 """ 1715 Read a long long value from memory 1716 1717 Args: 1718 - address: address to read (Int) 1719 1720 Returns: 1721 - mem value (Long Long) 1722 """ 1723 return self.read_int(address, 8) 1724 1725 def writemem(self, address, buf): 1726 """ 1727 Write buf to memory start at an address 1728 1729 Args: 1730 - address: start address to write (Int) 1731 - buf: data to write (raw bytes) 1732 1733 Returns: 1734 - number of written bytes (Int) 1735 """ 1736 out = None 1737 if not buf: 1738 return 0 1739 1740 if self.getpid(): 1741 # try fast restore mem 1742 tmp = tmpfile(is_binary_file=True) 1743 tmp.write(buf) 1744 tmp.flush() 1745 out = self.execute_redirect("restore %s binary 0x%x" % (tmp.name, address)) 1746 tmp.close() 1747 if not out: # try the slow way 1748 for i in range(len(buf)): 1749 if not self.execute("set {char}0x%x = 0x%x" % (address+i, ord(buf[i]))): 1750 return i 1751 return i+1 1752 elif "error" in out: # failed to write the whole buf, find written byte 1753 for i in range(0, len(buf), 1): 1754 if not self.is_address(address+i): 1755 return i 1756 else: 1757 return len(buf) 1758 1759 def write_int(self, address, value, intsize=None): 1760 """ 1761 Write an interger value to memory 1762 1763 Args: 1764 - address: address to read (Int) 1765 - value: int to write to (Int) 1766 - intsize: force write size (Int) 1767 1768 Returns: 1769 - Bool 1770 """ 1771 if not intsize: 1772 intsize = self.intsize() 1773 buf = hex2str(value, intsize).ljust(intsize, "\x00")[:intsize] 1774 saved = self.readmem(address, intsize) 1775 if not saved: 1776 return False 1777 1778 ret = self.writemem(address, buf) 1779 if ret != intsize: 1780 self.writemem(address, saved) 1781 return False 1782 return True 1783 1784 def write_long(self, address, value): 1785 """ 1786 Write a long long value to memory 1787 1788 Args: 1789 - address: address to read (Int) 1790 - value: value to write to 1791 1792 Returns: 1793 - Bool 1794 """ 1795 return self.write_int(address, value, 8) 1796 1797 def cmpmem(self, start, end, buf): 1798 """ 1799 Compare contents of a memory region with a buffer 1800 1801 Args: 1802 - start: start address (Int) 1803 - end: end address (Int) 1804 - buf: raw bytes 1805 1806 Returns: 1807 - dictionary of array of diffed bytes in hex (Dictionary) 1808 {123: [("A", "B"), ("C", "C"))]} 1809 """ 1810 line_len = 32 1811 if end < start: 1812 (start, end) = (end, start) 1813 1814 mem = self.dumpmem(start, end) 1815 if mem is None: 1816 return None 1817 1818 length = min(len(mem), len(buf)) 1819 result = {} 1820 lineno = 0 1821 for i in range(length//line_len): 1822 diff = 0 1823 bytes_ = [] 1824 for j in range(line_len): 1825 offset = i*line_len+j 1826 bytes_ += [(mem[offset:offset + 1], buf[offset:offset + 1])] 1827 if mem[offset] != buf[offset]: 1828 diff = 1 1829 if diff == 1: 1830 result[start+lineno] = bytes_ 1831 lineno += line_len 1832 1833 bytes_ = [] 1834 diff = 0 1835 for i in range(length % line_len): 1836 offset = lineno+i 1837 bytes_ += [(mem[offset:offset + 1], buf[offset:offset + 1])] 1838 if mem[offset] != buf[offset]: 1839 diff = 1 1840 if diff == 1: 1841 result[start+lineno] = bytes_ 1842 1843 return result 1844 1845 def xormem(self, start, end, key): 1846 """ 1847 XOR a memory region with a key 1848 1849 Args: 1850 - start: start address (Int) 1851 - end: end address (Int) 1852 - key: XOR key (String) 1853 1854 Returns: 1855 - xored memory content (raw bytes) 1856 """ 1857 mem = self.dumpmem(start, end) 1858 if mem is None: 1859 return None 1860 1861 if to_int(key) != None: 1862 key = hex2str(to_int(key), self.intsize()) 1863 mem = list(bytes_iterator(mem)) 1864 for index, char in enumerate(mem): 1865 key_idx = index % len(key) 1866 mem[index] = chr(ord(char) ^ ord(key[key_idx])) 1867 1868 buf = b"".join([to_binary_string(x) for x in mem]) 1869 bytes = self.writemem(start, buf) 1870 return buf 1871 1872 def searchmem(self, start, end, search, mem=None): 1873 """ 1874 Search for all instances of a pattern in memory from start to end 1875 1876 Args: 1877 - start: start address (Int) 1878 - end: end address (Int) 1879 - search: string or python regex pattern (String) 1880 - mem: cached mem to not re-read for repeated searches (raw bytes) 1881 1882 Returns: 1883 - list of found result: (address(Int), hex encoded value(String)) 1884 1885 """ 1886 1887 result = [] 1888 if end < start: 1889 (start, end) = (end, start) 1890 1891 if mem is None: 1892 mem = self.dumpmem(start, end) 1893 1894 if not mem: 1895 return result 1896 1897 if isinstance(search, six.string_types) and search.startswith("0x"): 1898 # hex number 1899 search = search[2:] 1900 if len(search) %2 != 0: 1901 search = "0" + search 1902 search = codecs.decode(search, 'hex')[::-1] 1903 search = re.escape(search) 1904 1905 # Convert search to bytes if is not already 1906 if not isinstance(search, bytes): 1907 search = search.encode('utf-8') 1908 1909 try: 1910 p = re.compile(search) 1911 except: 1912 search = re.escape(search) 1913 p = re.compile(search) 1914 1915 found = list(p.finditer(mem)) 1916 for m in found: 1917 index = 1 1918 if m.start() == m.end() and m.lastindex: 1919 index = m.lastindex+1 1920 for i in range(0,index): 1921 if m.start(i) != m.end(i): 1922 result += [(start + m.start(i), codecs.encode(mem[m.start(i):m.end(i)], 'hex'))] 1923 1924 return result 1925 1926 def searchmem_by_range(self, mapname, search): 1927 """ 1928 Search for all instances of a pattern in virtual memory ranges 1929 1930 Args: 1931 - search: string or python regex pattern (String) 1932 - mapname: name of virtual memory range (String) 1933 1934 Returns: 1935 - list of found result: (address(Int), hex encoded value(String)) 1936 """ 1937 1938 result = [] 1939 ranges = self.get_vmmap(mapname) 1940 if ranges: 1941 for (start, end, perm, name) in ranges: 1942 if "r" in perm: 1943 result += self.searchmem(start, end, search) 1944 1945 return result 1946 1947 @memoized 1948 def search_reference(self, search, mapname=None): 1949 """ 1950 Search for all references to a value in memory ranges 1951 1952 Args: 1953 - search: string or python regex pattern (String) 1954 - mapname: name of target virtual memory range (String) 1955 1956 Returns: 1957 - list of found result: (address(int), hex encoded value(String)) 1958 """ 1959 1960 maps = self.get_vmmap() 1961 ranges = self.get_vmmap(mapname) 1962 result = [] 1963 search_result = [] 1964 for (start, end, perm, name) in maps: 1965 if "r" in perm: 1966 search_result += self.searchmem(start, end, search) 1967 1968 for (start, end, perm, name) in ranges: 1969 for (a, v) in search_result: 1970 result += self.searchmem(start, end, to_address(a)) 1971 1972 return result 1973 1974 @memoized 1975 def search_address(self, searchfor="stack", belongto="binary"): 1976 """ 1977 Search for all valid addresses in memory ranges 1978 1979 Args: 1980 - searchfor: memory region to search for addresses (String) 1981 - belongto: memory region that target addresses belong to (String) 1982 1983 Returns: 1984 - list of found result: (address(Int), value(Int)) 1985 """ 1986 1987 result = [] 1988 maps = self.get_vmmap() 1989 if maps is None: 1990 return result 1991 1992 searchfor_ranges = self.get_vmmap(searchfor) 1993 belongto_ranges = self.get_vmmap(belongto) 1994 step = self.intsize() 1995 for (start, end, _, _) in searchfor_ranges[::-1]: # dirty trick, to search in rw-p mem first 1996 mem = self.dumpmem(start, end) 1997 if not mem: 1998 continue 1999 for i in range(0, len(mem), step): 2000 search = "0x" + codecs.encode(mem[i:i+step][::-1], 'hex').decode('utf-8') 2001 addr = to_int(search) 2002 if self.is_address(addr, belongto_ranges): 2003 result += [(start+i, addr)] 2004 2005 return result 2006 2007 @memoized 2008 def search_pointer(self, searchfor="stack", belongto="binary"): 2009 """ 2010 Search for all valid pointers in memory ranges 2011 2012 Args: 2013 - searchfor: memory region to search for pointers (String) 2014 - belongto: memory region that pointed addresses belong to (String) 2015 2016 Returns: 2017 - list of found result: (address(Int), value(Int)) 2018 """ 2019 2020 search_result = [] 2021 result = [] 2022 maps = self.get_vmmap() 2023 searchfor_ranges = self.get_vmmap(searchfor) 2024 belongto_ranges = self.get_vmmap(belongto) 2025 step = self.intsize() 2026 for (start, end, _, _) in searchfor_ranges[::-1]: 2027 mem = self.dumpmem(start, end) 2028 if not mem: 2029 continue 2030 for i in range(0, len(mem), step): 2031 search = "0x" + codecs.encode(mem[i:i+step][::-1], 'hex').decode('utf-8') 2032 addr = to_int(search) 2033 if self.is_address(addr): 2034 (v, t, vn) = self.examine_mem_value(addr) 2035 if t != 'value': 2036 if self.is_address(to_int(vn), belongto_ranges): 2037 if (to_int(v), v) not in search_result: 2038 search_result += [(to_int(v), v)] 2039 2040 for (a, v) in search_result: 2041 result += self.searchmem(start, end, to_address(a), mem) 2042 2043 return result 2044 2045 @memoized 2046 def examine_mem_value(self, value): 2047 """ 2048 Examine a value in memory for its type and reference 2049 2050 Args: 2051 - value: value to examine (Int) 2052 2053 Returns: 2054 - tuple of (value(Int), type(String), next_value(Int)) 2055 """ 2056 def examine_data(value, bits=32): 2057 out = self.execute_redirect("x/%sx 0x%x" % ("g" if bits == 64 else "w", value)) 2058 if out: 2059 v = out.split(":\t")[-1].strip() 2060 if is_printable(int2hexstr(to_int(v), bits//8)): 2061 out = self.execute_redirect("x/s 0x%x" % value) 2062 return out 2063 2064 result = (None, None, None) 2065 if value is None: 2066 return result 2067 2068 maps = self.get_vmmap() 2069 binmap = self.get_vmmap("binary") 2070 2071 (arch, bits) = self.getarch() 2072 if not self.is_address(value): # a value 2073 result = (to_hex(value), "value", "") 2074 return result 2075 else: 2076 (_, _, _, mapname) = self.get_vmrange(value) 2077 2078 # check for writable first so rwxp mem will be treated as data 2079 if self.is_writable(value): # writable data address 2080 out = examine_data(value, bits) 2081 if out: 2082 result = (to_hex(value), "data", out.split(":", 1)[1].strip()) 2083 2084 elif self.is_executable(value): # code/rodata address 2085 if self.is_address(value, binmap): 2086 headers = self.elfheader() 2087 else: 2088 headers = self.elfheader_solib(mapname) 2089 2090 if headers: 2091 headers = sorted(headers.items(), key=lambda x: x[1][1]) 2092 for (k, (start, end, type)) in headers: 2093 if value >= start and value < end: 2094 if type == "code": 2095 out = self.get_disasm(value) 2096 p = re.compile(".*?0x[^ ]*?\s(.*)") 2097 m = p.search(out) 2098 result = (to_hex(value), "code", m.group(1)) 2099 else: # rodata address 2100 out = examine_data(value, bits) 2101 result = (to_hex(value), "rodata", out.split(":", 1)[1].strip()) 2102 break 2103 2104 if result[0] is None: # not fall to any header section 2105 out = examine_data(value, bits) 2106 result = (to_hex(value), "rodata", out.split(":", 1)[1].strip()) 2107 2108 else: # not belong to any lib: [heap], [vdso], [vsyscall], etc 2109 out = self.get_disasm(value) 2110 if "(bad)" in out: 2111 out = examine_data(value, bits) 2112 result = (to_hex(value), "rodata", out.split(":", 1)[1].strip()) 2113 else: 2114 p = re.compile(".*?0x[^ ]*?\s(.*)") 2115 m = p.search(out) 2116 result = (to_hex(value), "code", m.group(1)) 2117 2118 else: # readonly data address 2119 out = examine_data(value, bits) 2120 if out: 2121 result = (to_hex(value), "rodata", out.split(":", 1)[1].strip()) 2122 else: 2123 result = (to_hex(value), "rodata", "MemError") 2124 2125 return result 2126 2127 @memoized 2128 def examine_mem_reference(self, value, depth=5): 2129 """ 2130 Deeply examine a value in memory for its references 2131 2132 Args: 2133 - value: value to examine (Int) 2134 2135 Returns: 2136 - list of tuple of (value(Int), type(String), next_value(Int)) 2137 """ 2138 result = [] 2139 if depth <= 0: 2140 depth = 0xffffffff 2141 2142 (v, t, vn) = self.examine_mem_value(value) 2143 while vn is not None: 2144 if len(result) > depth: 2145 _v, _t, _vn = result[-1] 2146 result[-1] = (_v, _t, "--> ...") 2147 break 2148 2149 result += [(v, t, vn)] 2150 if v == vn or to_int(v) == to_int(vn): # point to self 2151 break 2152 if to_int(vn) is None: 2153 break 2154 if to_int(vn) in [to_int(v) for (v, _, _) in result]: # point back to previous value 2155 break 2156 (v, t, vn) = self.examine_mem_value(to_int(vn)) 2157 2158 return result 2159 2160 @memoized 2161 def format_search_result(self, result, display=256): 2162 """ 2163 Format the result from various memory search commands 2164 2165 Args: 2166 - result: result of search commands (List) 2167 - display: number of items to display 2168 2169 Returns: 2170 - text: formatted text (String) 2171 """ 2172 2173 text = "" 2174 if not result: 2175 text = "Not found" 2176 else: 2177 maxlen = 0 2178 maps = self.get_vmmap() 2179 shortmaps = [] 2180 for (start, end, perm, name) in maps: 2181 shortname = os.path.basename(name) 2182 if shortname.startswith("lib"): 2183 shortname = shortname.split("-")[0] 2184 shortmaps += [(start, end, perm, shortname)] 2185 2186 count = len(result) 2187 if display != 0: 2188 count = min(count, display) 2189 text += "Found %d results, display max %d items:\n" % (len(result), count) 2190 for (addr, v) in result[:count]: 2191 vmrange = self.get_vmrange(addr, shortmaps) 2192 maxlen = max(maxlen, len(vmrange[3])) 2193 2194 for (addr, v) in result[:count]: 2195 vmrange = self.get_vmrange(addr, shortmaps) 2196 chain = self.examine_mem_reference(addr) 2197 text += "%s : %s" % (vmrange[3].rjust(maxlen), format_reference_chain(chain) + "\n") 2198 2199 return text 2200 2201 2202 ########################## 2203 # Exploit Helpers # 2204 ########################## 2205 @memoized 2206 def elfentry(self): 2207 """ 2208 Get entry point address of debugged ELF file 2209 2210 Returns: 2211 - entry address (Int) 2212 """ 2213 out = self.execute_redirect("info files") 2214 p = re.compile("Entry point: ([^\s]*)") 2215 if out: 2216 m = p.search(out) 2217 if m: 2218 return to_int(m.group(1)) 2219 return None 2220 2221 @memoized 2222 def elfheader(self, name=None): 2223 """ 2224 Get headers information of debugged ELF file 2225 2226 Args: 2227 - name: specific header name (String) 2228 2229 Returns: 2230 - dictionary of headers {name(String): (start(Int), end(Int), type(String))} 2231 """ 2232 elfinfo = {} 2233 elfbase = 0 2234 if self.getpid(): 2235 binmap = self.get_vmmap("binary") 2236 elfbase = binmap[0][0] if binmap else 0 2237 2238 out = self.execute_redirect("maintenance info sections") 2239 if not out: 2240 return {} 2241 2242 p = re.compile("\s*(0x[^-]*)->(0x[^ ]*) at (0x[^:]*):\s*([^ ]*)\s*(.*)") 2243 matches = p.findall(out) 2244 2245 for (start, end, offset, hname, attr) in matches: 2246 start, end, offset = to_int(start), to_int(end), to_int(offset) 2247 # skip unuseful header 2248 if start < offset: 2249 continue 2250 # if PIE binary, update with runtime address 2251 if start < elfbase: 2252 start += elfbase 2253 end += elfbase 2254 2255 if "CODE" in attr: 2256 htype = "code" 2257 elif "READONLY" in attr: 2258 htype = "rodata" 2259 else: 2260 htype = "data" 2261 2262 elfinfo[hname.strip()] = (start, end, htype) 2263 2264 result = {} 2265 if name is None: 2266 result = elfinfo 2267 else: 2268 if name in elfinfo: 2269 result[name] = elfinfo[name] 2270 else: 2271 for (k, v) in elfinfo.items(): 2272 if name in k: 2273 result[k] = v 2274 return result 2275 2276 @memoized 2277 def elfsymbols(self, pattern=None): 2278 """ 2279 Get all non-debugging symbol information of debugged ELF file 2280 2281 Returns: 2282 - dictionary of (address(Int), symname(String)) 2283 """ 2284 headers = self.elfheader() 2285 if ".plt" not in headers: # static binary 2286 return {} 2287 2288 binmap = self.get_vmmap("binary") 2289 elfbase = binmap[0][0] if binmap else 0 2290 2291 # get the .dynstr header 2292 headers = self.elfheader() 2293 if ".dynstr" not in headers: 2294 return {} 2295 (start, end, _) = headers[".dynstr"] 2296 mem = self.dumpmem(start, end) 2297 if not mem and self.getfile(): 2298 fd = open(self.getfile()) 2299 fd.seek(start, 0) 2300 mem = fd.read(end-start) 2301 fd.close() 2302 2303 # Convert names into strings 2304 dynstrings = [name.decode('utf-8') for name in mem.split(b"\x00")] 2305 2306 if pattern: 2307 dynstrings = [s for s in dynstrings if re.search(pattern, s)] 2308 2309 # get symname@plt info 2310 symbols = {} 2311 for symname in dynstrings: 2312 if not symname: continue 2313 symname += "@plt" 2314 out = self.execute_redirect("info functions %s" % symname) 2315 if not out: continue 2316 m = re.findall(".*(0x[^ ]*)\s*%s" % re.escape(symname), out) 2317 for addr in m: 2318 addr = to_int(addr) 2319 if self.is_address(addr, binmap): 2320 if symname not in symbols: 2321 symbols[symname] = addr 2322 break 2323 2324 # if PIE binary, update with runtime address 2325 for (k, v) in symbols.items(): 2326 if v < elfbase: 2327 symbols[k] = v + elfbase 2328 2329 return symbols 2330 2331 @memoized 2332 def elfsymbol(self, symname=None): 2333 """ 2334 Get non-debugging symbol information of debugged ELF file 2335 2336 Args: 2337 - name: target function name (String), special cases: 2338 + "data": data transfer functions 2339 + "exec": exec helper functions 2340 2341 Returns: 2342 - if exact name is not provided: dictionary of tuple (symname, plt_entry) 2343 - if exact name is provided: dictionary of tuple (symname, plt_entry, got_entry, reloc_entry) 2344 """ 2345 datafuncs = ["printf", "puts", "gets", "cpy"] 2346 execfuncs = ["system", "exec", "mprotect", "mmap", "syscall"] 2347 result = {} 2348 if not symname or symname in ["data", "exec"]: 2349 symbols = self.elfsymbols() 2350 else: 2351 symbols = self.elfsymbols(symname) 2352 2353 if not symname: 2354 result = symbols 2355 else: 2356 sname = symname.replace("@plt", "") + "@plt" 2357 if sname in symbols: 2358 plt_addr = symbols[sname] 2359 result[sname] = plt_addr # plt entry 2360 out = self.get_disasm(plt_addr, 2) 2361 for line in out.splitlines(): 2362 if "jmp" in line: 2363 addr = to_int("0x" + line.strip().rsplit("0x")[-1].split()[0]) 2364 result[sname.replace("@plt","@got")] = addr # got entry 2365 if "push" in line: 2366 addr = to_int("0x" + line.strip().rsplit("0x")[-1]) 2367 result[sname.replace("@plt","@reloc")] = addr # reloc offset 2368 else: 2369 keywords = [symname] 2370 if symname == "data": 2371 keywords = datafuncs 2372 if symname == "exec": 2373 keywords = execfuncs 2374 for (k, v) in symbols.items(): 2375 for f in keywords: 2376 if f in k: 2377 result[k] = v 2378 2379 return result 2380 2381 @memoized 2382 def main_entry(self): 2383 """ 2384 Get address of main function of stripped ELF file 2385 2386 Returns: 2387 - main function address (Int) 2388 """ 2389 refs = self.xrefs("__libc_start_main@plt") 2390 if refs: 2391 inst = self.prev_inst(refs[0][0]) 2392 if inst: 2393 addr = re.search(".*(0x.*)", inst[0][1]) 2394 if addr: 2395 return to_int(addr.group(1)) 2396 return None 2397 2398 @memoized 2399 def readelf_header(self, filename, name=None): 2400 """ 2401 Get headers information of an ELF file using 'readelf' 2402 2403 Args: 2404 - filename: ELF file (String) 2405 - name: specific header name (String) 2406 2407 Returns: 2408 - dictionary of headers (name(String), value(Int)) (Dict) 2409 """ 2410 elfinfo = {} 2411 vmap = self.get_vmmap(filename) 2412 elfbase = vmap[0][0] if vmap else 0 2413 out = execute_external_command("%s -W -S %s" % (config.READELF, filename)) 2414 if not out: 2415 return {} 2416 p = re.compile(".*\[.*\] (\.[^ ]*) [^0-9]* ([^ ]*) [^ ]* ([^ ]*)(.*)") 2417 matches = p.findall(out) 2418 if not matches: 2419 return result 2420 2421 for (hname, start, size, attr) in matches: 2422 start, end = to_int("0x"+start), to_int("0x"+start) + to_int("0x"+size) 2423 # if PIE binary or DSO, update with runtime address 2424 if start < elfbase: 2425 start += elfbase 2426 if end < elfbase: 2427 end += elfbase 2428 2429 if "X" in attr: 2430 htype = "code" 2431 elif "W" in attr: 2432 htype = "data" 2433 else: 2434 htype = "rodata" 2435 elfinfo[hname.strip()] = (start, end, htype) 2436 2437 result = {} 2438 if name is None: 2439 result = elfinfo 2440 else: 2441 if name in elfinfo: 2442 result[name] = elfinfo[name] 2443 else: 2444 for (k, v) in elfinfo.items(): 2445 if name in k: 2446 result[k] = v 2447 return result 2448 2449 @memoized 2450 def elfheader_solib(self, solib=None, name=None): 2451 """ 2452 Get headers information of Shared Object Libraries linked to target 2453 2454 Args: 2455 - solib: shared library name (String) 2456 - name: specific header name (String) 2457 2458 Returns: 2459 - dictionary of headers {name(String): start(Int), end(Int), type(String)) 2460 """ 2461 # hardcoded ELF header type 2462 header_type = {"code": [".text", ".fini", ".init", ".plt", "__libc_freeres_fn"], 2463 "data": [".dynamic", ".data", ".ctors", ".dtors", ".jrc", ".got", ".got.plt", 2464 ".bss", ".tdata", ".tbss", ".data.rel.ro", ".fini_array", 2465 "__libc_subfreeres", "__libc_thread_subfreeres"] 2466 } 2467 2468 @memoized 2469 def _elfheader_solib_all(): 2470 out = self.execute_redirect("info files") 2471 if not out: 2472 return None 2473 2474 p = re.compile("[^\n]*\s*(0x[^ ]*) - (0x[^ ]*) is (\.[^ ]*) in (.*)") 2475 soheaders = p.findall(out) 2476 2477 result = [] 2478 for (start, end, hname, libname) in soheaders: 2479 start, end = to_int(start), to_int(end) 2480 result += [(start, end, hname, os.path.realpath(libname))] # tricky, return the realpath version of libraries 2481 return result 2482 2483 elfinfo = {} 2484 2485 headers = _elfheader_solib_all() 2486 if not headers: 2487 return {} 2488 2489 if solib is None: 2490 return headers 2491 2492 vmap = self.get_vmmap(solib) 2493 elfbase = vmap[0][0] if vmap else 0 2494 2495 for (start, end, hname, libname) in headers: 2496 if solib in libname: 2497 # if PIE binary or DSO, update with runtime address 2498 if start < elfbase: 2499 start += elfbase 2500 if end < elfbase: 2501 end += elfbase 2502 # determine the type 2503 htype = "rodata" 2504 if hname in header_type["code"]: 2505 htype = "code" 2506 elif hname in header_type["data"]: 2507 htype = "data" 2508 elfinfo[hname.strip()] = (start, end, htype) 2509 2510 result = {} 2511 if name is None: 2512 result = elfinfo 2513 else: 2514 if name in elfinfo: 2515 result[name] = elfinfo[name] 2516 else: 2517 for (k, v) in elfinfo.items(): 2518 if name in k: 2519 result[k] = v 2520 return result 2521 2522 def checksec(self, filename=None): 2523 """ 2524 Check for various security options of binary (ref: http://www.trapkit.de/tools/checksec.sh) 2525 2526 Args: 2527 - file: path name of file to check (String) 2528 2529 Returns: 2530 - dictionary of (setting(String), status(Int)) (Dict) 2531 """ 2532 result = {} 2533 result["RELRO"] = 0 2534 result["CANARY"] = 0 2535 result["NX"] = 1 2536 result["PIE"] = 0 2537 result["FORTIFY"] = 0 2538 2539 if filename is None: 2540 filename = self.getfile() 2541 2542 if not filename: 2543 return None 2544 2545 out = execute_external_command("%s -W -a \"%s\" 2>&1" % (config.READELF, filename)) 2546 if "Error:" in out: 2547 return None 2548 2549 for line in out.splitlines(): 2550 if "GNU_RELRO" in line: 2551 result["RELRO"] |= 2 2552 if "BIND_NOW" in line: 2553 result["RELRO"] |= 1 2554 if "__stack_chk_fail" in line: 2555 result["CANARY"] = 1 2556 if "GNU_STACK" in line and "RWE" in line: 2557 result["NX"] = 0 2558 if "Type:" in line and "DYN (" in line: 2559 result["PIE"] = 4 # Dynamic Shared Object 2560 if "(DEBUG)" in line and result["PIE"] == 4: 2561 result["PIE"] = 1 2562 if "_chk@" in line: 2563 result["FORTIFY"] = 1 2564 2565 if result["RELRO"] == 1: 2566 result["RELRO"] = 0 # ? | BIND_NOW + NO GNU_RELRO = NO PROTECTION 2567 # result["RELRO"] == 2 # Partial | NO BIND_NOW + GNU_RELRO 2568 # result["RELRO"] == 3 # Full | BIND_NOW + GNU_RELRO 2569 return result 2570 2571 def _verify_rop_gadget(self, start, end, depth=5): 2572 """ 2573 Verify ROP gadget code from start to end with max number of instructions 2574 2575 Args: 2576 - start: start address (Int) 2577 - end: end addres (Int) 2578 - depth: number of instructions (Int) 2579 2580 Returns: 2581 - list of valid gadgets (address(Int), asmcode(String)) 2582 """ 2583 2584 result = [] 2585 valid = 0 2586 out = self.execute_redirect("disassemble 0x%x, 0x%x" % (start, end+1)) 2587 if not out: 2588 return [] 2589 2590 code = out.splitlines()[1:-1] 2591 for line in code: 2592 if "bad" in line: 2593 return [] 2594 (addr, code) = line.strip().split(":", 1) 2595 addr = to_int(addr.split()[0]) 2596 result += [(addr, " ".join(code.strip().split()))] 2597 if "ret" in code: 2598 return result 2599 if len(result) > depth: 2600 break 2601 2602 return [] 2603 2604 @memoized 2605 def search_asm(self, start, end, asmcode, rop=0): 2606 """ 2607 Search for ASM instructions in memory 2608 2609 Args: 2610 - start: start address (Int) 2611 - end: end address (Int) 2612 - asmcode: assembly instruction (String) 2613 + multiple instructions are separated by ";" 2614 + wildcard ? supported, will be replaced by registers or multi-bytes 2615 2616 Returns: 2617 - list of (address(Int), hexbyte(String)) 2618 """ 2619 wildcard = asmcode.count('?') 2620 magic_bytes = ["0x00", "0xff", "0xdead", "0xdeadbeef", "0xdeadbeefdeadbeef"] 2621 2622 ops = [x for x in asmcode.split(';') if x] 2623 def buildcode(code=b"", pos=0, depth=0): 2624 if depth == wildcard and pos == len(ops): 2625 yield code 2626 return 2627 2628 c = ops[pos].count('?') 2629 if c > 2: return 2630 elif c == 0: 2631 asm = self.assemble(ops[pos]) 2632 if asm: 2633 for code in buildcode(code + asm, pos+1, depth): 2634 yield code 2635 else: 2636 save = ops[pos] 2637 for regs in REGISTERS.values(): 2638 for reg in regs: 2639 ops[pos] = save.replace("?", reg, 1) 2640 for asmcode_reg in buildcode(code, pos, depth+1): 2641 yield asmcode_reg 2642 for byte in magic_bytes: 2643 ops[pos] = save.replace("?", byte, 1) 2644 for asmcode_mem in buildcode(code, pos, depth+1): 2645 yield asmcode_mem 2646 ops[pos] = save 2647 2648 searches = [] 2649 2650 def decode_hex_escape(str_): 2651 """Decode string as hex and escape for regex""" 2652 return re.escape(codecs.decode(str_, 'hex')) 2653 2654 for machine_code in buildcode(): 2655 search = re.escape(machine_code) 2656 search = search.replace(decode_hex_escape(b"dead"), b"..")\ 2657 .replace(decode_hex_escape(b"beef"), b"..")\ 2658 .replace(decode_hex_escape(b"00"), b".")\ 2659 .replace(decode_hex_escape(b"ff"), b".") 2660 2661 if rop and 'ret' not in asmcode: 2662 search += b".{0,24}\\xc3" 2663 searches.append(search) 2664 2665 if not searches: 2666 warning_msg("invalid asmcode: '%s'" % asmcode) 2667 return [] 2668 2669 search = b"(?=(" + b"|".join(searches) + b"))" 2670 candidates = self.searchmem(start, end, search) 2671 2672 if rop: 2673 result = {} 2674 for (a, v) in candidates: 2675 gadget = self._verify_rop_gadget(a, a+len(v)//2 - 1) 2676 # gadget format: [(address, asmcode), (address, asmcode), ...] 2677 if gadget != []: 2678 blen = gadget[-1][0] - gadget[0][0] + 1 2679 bytes = v[:2*blen] 2680 asmcode_rs = "; ".join([c for _, c in gadget]) 2681 if re.search(re.escape(asmcode).replace("\ ",".*").replace("\?",".*"), asmcode_rs)\ 2682 and a not in result: 2683 result[a] = (bytes, asmcode_rs) 2684 result = list(result.items()) 2685 else: 2686 result = [] 2687 for (a, v) in candidates: 2688 asmcode = self.execute_redirect("disassemble 0x%x, 0x%x" % (a, a+(len(v)//2))) 2689 if asmcode: 2690 asmcode = "\n".join(asmcode.splitlines()[1:-1]) 2691 matches = re.findall(".*:([^\n]*)", asmcode) 2692 result += [(a, (v, ";".join(matches).strip()))] 2693 2694 return result 2695 2696 def dumprop(self, start, end, keyword=None, depth=5): 2697 """ 2698 Dump unique ROP gadgets in memory 2699 2700 Args: 2701 - start: start address (Int) 2702 - end: end address (Int) 2703 - keyword: to match start of gadgets (String) 2704 2705 Returns: 2706 - dictionary of (address(Int), asmcode(String)) 2707 """ 2708 2709 EXTRA_WORDS = ["BYTE ", " WORD", "DWORD ", "FWORD ", "QWORD ", "PTR ", "FAR "] 2710 result = {} 2711 mem = self.dumpmem(start, end) 2712 if mem is None: 2713 return {} 2714 2715 if keyword: 2716 search = keyword 2717 else: 2718 search = "" 2719 2720 if len(mem) > 20000: # limit backward depth if searching in large mem 2721 depth = 3 2722 found = re.finditer(b"\xc3", mem) 2723 found = list(found) 2724 for m in found: 2725 idx = start+m.start() 2726 for i in range(1, 24): 2727 gadget = self._verify_rop_gadget(idx-i, idx, depth) 2728 if gadget != []: 2729 k = "; ".join([v for (a, v) in gadget]) 2730 if k.startswith(search): 2731 for w in EXTRA_WORDS: 2732 k = k.replace(w, "") 2733 if k not in result: 2734 result[k] = gadget[0][0] 2735 return result 2736 2737 def common_rop_gadget(self, mapname=None): 2738 """ 2739 Get common rop gadgets in binary: ret, popret, pop2ret, pop3ret, add [mem] reg, add reg [mem] 2740 2741 Returns: 2742 - dictionary of (gadget(String), address(Int)) 2743 """ 2744 2745 def _valid_register_opcode(bytes_): 2746 if not bytes_: 2747 return False 2748 2749 for c in bytes_iterator(bytes_): 2750 if ord(c) not in list(range(0x58, 0x60)): 2751 return False 2752 return True 2753 2754 result = {} 2755 if mapname is None: 2756 mapname = "binary" 2757 maps = self.get_vmmap(mapname) 2758 if maps is None: 2759 return result 2760 2761 for (start, end, _, _) in maps: 2762 if not self.is_executable(start, maps): continue 2763 2764 mem = self.dumpmem(start, end) 2765 found = self.searchmem(start, end, b"....\xc3", mem) 2766 for (a, v) in found: 2767 v = codecs.decode(v, 'hex') 2768 if "ret" not in result: 2769 result["ret"] = a+4 2770 if "leaveret" not in result: 2771 if v[-2] == "\xc9": 2772 result["leaveret"] = a+3 2773 if "popret" not in result: 2774 if _valid_register_opcode(v[-2:-1]): 2775 result["popret"] = a+3 2776 if "pop2ret" not in result: 2777 if _valid_register_opcode(v[-3:-1]): 2778 result["pop2ret"] = a+2 2779 if "pop3ret" not in result: 2780 if _valid_register_opcode(v[-4:-1]): 2781 result["pop3ret"] = a+1 2782 if "pop4ret" not in result: 2783 if _valid_register_opcode(v[-5:-1]): 2784 result["pop4ret"] = a 2785 2786 # search for add esp, byte 0xNN 2787 found = self.searchmem(start, end, b"\x83\xc4([^\xc3]){0,24}\xc3", mem) 2788 # search for add esp, 0xNNNN 2789 found += self.searchmem(start, end, b"\x81\xc4([^\xc3]){0,24}\xc3", mem) 2790 for (a, v) in found: 2791 if v.startswith(b"81"): 2792 offset = to_int("0x" + codecs.encode(codecs.decode(v, 'hex')[2:5][::-1], 'hex').decode('utf-8')) 2793 elif v.startswith(b"83"): 2794 offset = to_int("0x" + v[4:6].decode('utf-8')) 2795 gg = self._verify_rop_gadget(a, a+len(v)//2-1) 2796 for (_, c) in gg: 2797 if "pop" in c: 2798 offset += 4 2799 gadget = "addesp_%d" % offset 2800 if gadget not in result: 2801 result[gadget] = a 2802 2803 return result 2804 2805 def search_jmpcall(self, start, end, regname=None): 2806 """ 2807 Search memory for jmp/call reg instructions 2808 2809 Args: 2810 - start: start address (Int) 2811 - end: end address (Int) 2812 - reg: register name (String) 2813 2814 Returns: 2815 - list of (address(Int), instruction(String)) 2816 """ 2817 2818 result = [] 2819 REG = {0: "eax", 1: "ecx", 2: "edx", 3: "ebx", 4: "esp", 5: "ebp", 6: "esi", 7:"edi"} 2820 P2REG = {0: "[eax]", 1: "[ecx]", 2: "[edx]", 3: "[ebx]", 6: "[esi]", 7:"[edi]"} 2821 OPCODE = {0xe: "jmp", 0xd: "call"} 2822 P2OPCODE = {0x1: "call", 0x2: "jmp"} 2823 JMPREG = [b"\xff" + bytes_chr(i) for i in range(0xe0, 0xe8)] 2824 JMPREG += [b"\xff" + bytes_chr(i) for i in range(0x20, 0x28)] 2825 CALLREG = [b"\xff" + bytes_chr(i) for i in range(0xd0, 0xd8)] 2826 CALLREG += [b"\xff" + bytes_chr(i) for i in range(0x10, 0x18)] 2827 JMPCALL = JMPREG + CALLREG 2828 2829 if regname is None: 2830 regname = "" 2831 regname = regname.lower() 2832 pattern = re.compile(b'|'.join(JMPCALL).replace(b' ', b'\ ')) 2833 mem = self.dumpmem(start, end) 2834 found = pattern.finditer(mem) 2835 (arch, bits) = self.getarch() 2836 for m in list(found): 2837 inst = "" 2838 addr = start + m.start() 2839 opcode = codecs.encode(m.group()[1:2], 'hex') 2840 type = int(opcode[0:1], 16) 2841 reg = int(opcode[1:2], 16) 2842 if type in OPCODE: 2843 inst = OPCODE[type] + " " + REG[reg] 2844 2845 if type in P2OPCODE and reg in P2REG: 2846 inst = P2OPCODE[type] + " " + P2REG[reg] 2847 2848 if inst != "" and regname[-2:] in inst.split()[-1]: 2849 if bits == 64: 2850 inst = inst.replace("e", "r") 2851 result += [(addr, inst)] 2852 2853 return result 2854 2855 def search_substr(self, start, end, search, mem=None): 2856 """ 2857 Search for substrings of a given string/number in memory 2858 2859 Args: 2860 - start: start address (Int) 2861 - end: end address (Int) 2862 - search: string to search for (String) 2863 - mem: cached memory (raw bytes) 2864 2865 Returns: 2866 - list of tuple (substr(String), address(Int)) 2867 """ 2868 def substr(s1, s2): 2869 "Search for a string in another string" 2870 s1 = to_binary_string(s1) 2871 s2 = to_binary_string(s2) 2872 i = 1 2873 found = 0 2874 while i <= len(s1): 2875 if s2.find(s1[:i]) != -1: 2876 found = 1 2877 i += 1 2878 if s1[:i-1][-1:] == b"\x00": 2879 break 2880 else: 2881 break 2882 if found == 1: 2883 return i-1 2884 else: 2885 return -1 2886 2887 result = [] 2888 if end < start: 2889 start, end = end, start 2890 2891 if mem is None: 2892 mem = self.dumpmem(start, end) 2893 2894 if search[:2] == "0x": # hex number 2895 search = search[2:] 2896 if len(search) %2 != 0: 2897 search = "0" + search 2898 search = codecs.decode(search, 'hex')[::-1] 2899 search = to_binary_string(decode_string_escape(search)) 2900 while search: 2901 l = len(search) 2902 i = substr(search, mem) 2903 if i != -1: 2904 sub = search[:i] 2905 addr = start + mem.find(sub) 2906 if not check_badchars(addr): 2907 result.append((sub, addr)) 2908 else: 2909 result.append((search, -1)) 2910 return result 2911 search = search[i:] 2912 return result 2913 2914 2915 ############################## 2916 # ROP Payload Generation # 2917 ############################## 2918 def payload_copybytes(self, target=None, data=None, template=0): 2919 """ 2920 Suggest function for ret2plt exploit and generate payload for it 2921 2922 Args: 2923 - target: address to copy data to (Int) 2924 - data: (String) 2925 Returns: 2926 - python code template (String) 2927 """ 2928 result = "" 2929 funcs = ["strcpy", "sprintf", "strncpy", "snprintf", "memcpy"] 2930 2931 symbols = self.elfsymbols() 2932 transfer = "" 2933 for f in funcs: 2934 if f+"@plt" in symbols: 2935 transfer = f 2936 break 2937 if transfer == "": 2938 warning_msg("No copy function available") 2939 return None 2940 2941 headers = self.elfheader() 2942 start = min([v[0] for (k, v) in headers.items() if v[0] > 0]) 2943 end = max([v[1] for (k, v) in headers.items() if v[2] != "data"]) 2944 symbols = self.elfsymbol(transfer) 2945 if not symbols: 2946 warning_msg("Unable to find symbols") 2947 return None 2948 2949 plt_func = transfer + "_plt" 2950 plt_addr = symbols[transfer+"@plt"] 2951 gadgets = self.common_rop_gadget() 2952 function_template = "\n".join([ 2953 "popret = 0x%x" % gadgets["popret"], 2954 "pop2ret = 0x%x" % gadgets["pop2ret"], 2955 "pop3ret = 0x%x" % gadgets["pop3ret"], 2956 "def %s_payload(target, bytes):" % transfer, 2957 " %s = 0x%x" % (plt_func, plt_addr), 2958 " payload = []", 2959 " offset = 0", 2960 " for (str, addr) in bytes:", 2961 "", 2962 ]) 2963 if "ncp" in transfer or "mem" in transfer: # memcpy() style 2964 function_template += "\n".join([ 2965 " payload += [%s, pop3ret, target+offset, addr, len(str)]" % plt_func, 2966 " offset += len(str)", 2967 ]) 2968 elif "snp" in transfer: # snprintf() 2969 function_template += "\n".join([ 2970 " payload += [%s, pop3ret, target+offset, len(str)+1, addr]" % plt_func, 2971 " offset += len(str)", 2972 ]) 2973 else: 2974 function_template += "\n".join([ 2975 " payload += [%s, pop2ret, target+offset, addr]" % plt_func, 2976 " offset += len(str)", 2977 ]) 2978 function_template += "\n".join(["", 2979 " return payload", 2980 "", 2981 "payload = []" 2982 ]) 2983 2984 if target is None: 2985 if template != 0: 2986 return function_template 2987 else: 2988 return "" 2989 2990 #text = "\n_payload = []\n" 2991 text = "\n" 2992 mem = self.dumpmem(start, end) 2993 bytes = self.search_substr(start, end, data, mem) 2994 2995 if to_int(target) is not None: 2996 target = to_hex(target) 2997 text += "# %s <= %s\n" % (target, repr(data)) 2998 if not bytes: 2999 text += "***Failed***\n" 3000 else: 3001 text += "bytes = [\n" 3002 for (s, a) in bytes: 3003 if a != -1: 3004 text += " (%s, %s),\n" % (repr(s), to_hex(a)) 3005 else: 3006 text += " (%s, ***Failed***),\n" % repr(s) 3007 text += "\n".join([ 3008 "]", 3009 "payload += %s_payload(%s, bytes)" % (transfer, target), 3010 "", 3011 ]) 3012 3013 return text 3014 3015 3016########################################################################### 3017class PEDACmd(object): 3018 """ 3019 Class for PEDA commands that interact with GDB 3020 """ 3021 commands = [] 3022 def __init__(self): 3023 # list of all available commands 3024 self.commands = [c for c in dir(self) if callable(getattr(self, c)) and not c.startswith("_")] 3025 3026 ################## 3027 # Misc Utils # 3028 ################## 3029 def _missing_argument(self): 3030 """ 3031 Raise exception for missing argument, for internal use 3032 """ 3033 text = "missing argument" 3034 error_msg(text) 3035 raise Exception(text) 3036 3037 def _is_running(self): 3038 """ 3039 Check if program is running, for internal use 3040 """ 3041 pid = peda.getpid() 3042 if pid is None: 3043 text = "not running" 3044 warning_msg(text) 3045 return None 3046 #raise Exception(text) 3047 else: 3048 return pid 3049 3050 def reload(self, *arg): 3051 """ 3052 Reload PEDA sources, keep current options untouch 3053 Usage: 3054 MYNAME [name] 3055 """ 3056 (modname,) = normalize_argv(arg, 1) 3057 # save current PEDA options 3058 saved_opt = config.Option 3059 peda_path = os.path.dirname(PEDAFILE) + "/lib/" 3060 if not modname: 3061 modname = "PEDA" # just for notification 3062 ret = peda.execute("source %s" % PEDAFILE) 3063 else: 3064 if not modname.endswith(".py"): 3065 modname = modname + ".py" 3066 filepath = "%s/%s" % (peda_path, modname) 3067 if os.path.exists(filepath): 3068 ret = peda.execute("source %s" % filepath) 3069 peda.execute("source %s" % PEDAFILE) 3070 else: 3071 ret = False 3072 3073 config.Option = saved_opt 3074 if ret: 3075 msg("%s reloaded!" % modname, "blue") 3076 else: 3077 msg("Failed to reload %s source from: %s" % (modname, peda_path)) 3078 return 3079 3080 def _get_helptext(self, *arg): 3081 """ 3082 Get the help text, for internal use by help command and other aliases 3083 """ 3084 3085 (cmd,) = normalize_argv(arg, 1) 3086 helptext = "" 3087 if cmd is None: 3088 helptext = red("PEDA", "bold") + blue(" - Python Exploit Development Assistance for GDB", "bold") + "\n" 3089 helptext += "For latest update, check peda project page: %s\n" % green("https://github.com/longld/peda/") 3090 helptext += "List of \"peda\" subcommands, type the subcommand to invoke it:\n" 3091 i = 0 3092 for cmd in self.commands: 3093 if cmd.startswith("_"): continue # skip internal use commands 3094 func = getattr(self, cmd) 3095 helptext += "%s -- %s\n" % (cmd, green(trim(func.__doc__.strip("\n").splitlines()[0]))) 3096 helptext += "\nType \"help\" followed by subcommand for full documentation." 3097 else: 3098 if cmd in self.commands: 3099 func = getattr(self, cmd) 3100 lines = trim(func.__doc__).splitlines() 3101 helptext += green(lines[0]) + "\n" 3102 for line in lines[1:]: 3103 if "Usage:" in line: 3104 helptext += blue(line) + "\n" 3105 else: 3106 helptext += line + "\n" 3107 else: 3108 for c in self.commands: 3109 if not c.startswith("_") and cmd in c: 3110 func = getattr(self, c) 3111 helptext += "%s -- %s\n" % (c, green(trim(func.__doc__.strip("\n").splitlines()[0]))) 3112 3113 return helptext 3114 3115 def help(self, *arg): 3116 """ 3117 Print the usage manual for PEDA commands 3118 Usage: 3119 MYNAME 3120 MYNAME command 3121 """ 3122 3123 msg(self._get_helptext(*arg)) 3124 3125 return 3126 help.options = commands 3127 3128 def pyhelp(self, *arg): 3129 """ 3130 Wrapper for python built-in help 3131 Usage: 3132 MYNAME (enter interactive help) 3133 MYNAME help_request 3134 """ 3135 (request,) = normalize_argv(arg, 1) 3136 if request is None: 3137 help() 3138 return 3139 3140 peda_methods = ["%s" % c for c in dir(PEDA) if callable(getattr(PEDA, c)) and \ 3141 not c.startswith("_")] 3142 3143 if request in peda_methods: 3144 request = "peda.%s" % request 3145 try: 3146 if request.lower().startswith("peda"): 3147 request = eval(request) 3148 help(request) 3149 return 3150 3151 if "." in request: 3152 module, _, function = request.rpartition('.') 3153 if module: 3154 module = module.split(".")[0] 3155 __import__(module) 3156 mod = sys.modules[module] 3157 if function: 3158 request = getattr(mod, function) 3159 else: 3160 request = mod 3161 else: 3162 mod = sys.modules['__main__'] 3163 request = getattr(mod, request) 3164 3165 # wrapper for python built-in help 3166 help(request) 3167 except: # fallback to built-in help 3168 try: 3169 help(request) 3170 except Exception as e: 3171 if config.Option.get("debug") == "on": 3172 msg('Exception (%s): %s' % ('pyhelp', e), "red") 3173 traceback.print_exc() 3174 msg("no Python documentation found for '%s'" % request) 3175 3176 return 3177 pyhelp.options = ["%s" % c for c in dir(PEDA) if callable(getattr(PEDA, c)) and \ 3178 not c.startswith("_")] 3179 3180 # show [option | args | env] 3181 def show(self, *arg): 3182 """ 3183 Show various PEDA options and other settings 3184 Usage: 3185 MYNAME option [optname] 3186 MYNAME (show all options) 3187 MYNAME args 3188 MYNAME env [envname] 3189 """ 3190 # show options 3191 def _show_option(name=None): 3192 if name is None: 3193 name = "" 3194 filename = peda.getfile() 3195 if filename: 3196 filename = os.path.basename(filename) 3197 else: 3198 filename = None 3199 for (k, v) in sorted(config.Option.show(name).items()): 3200 if filename and isinstance(v, str) and "#FILENAME#" in v: 3201 v = v.replace("#FILENAME#", filename) 3202 msg("%s = %s" % (k, repr(v))) 3203 return 3204 3205 # show args 3206 def _show_arg(): 3207 arg = peda.execute_redirect("show args") 3208 arg = arg.split("started is ")[1][1:-3] 3209 arg = (peda.string_to_argv(arg)) 3210 if not arg: 3211 msg("No argument") 3212 for (i, a) in enumerate(arg): 3213 text = "arg[%d]: %s" % ((i+1), a if is_printable(a) else to_hexstr(a)) 3214 msg(text) 3215 return 3216 3217 # show envs 3218 def _show_env(name=None): 3219 if name is None: 3220 name = "" 3221 env = peda.execute_redirect("show env") 3222 for line in env.splitlines(): 3223 (k, v) = line.split("=", 1) 3224 if k.startswith(name): 3225 msg("%s = %s" % (k, v if is_printable(v) else to_hexstr(v))) 3226 return 3227 3228 (opt, name) = normalize_argv(arg, 2) 3229 3230 if opt is None or opt.startswith("opt"): 3231 _show_option(name) 3232 elif opt.startswith("arg"): 3233 _show_arg() 3234 elif opt.startswith("env"): 3235 _show_env(name) 3236 else: 3237 msg("Unknown show option: %s" % opt) 3238 return 3239 show.options = ["option", "arg", "env"] 3240 3241 # set [option | arg | env] 3242 def set(self, *arg): 3243 """ 3244 Set various PEDA options and other settings 3245 Usage: 3246 MYNAME option name value 3247 MYNAME arg string 3248 MYNAME env name value 3249 support input non-printable chars, e.g MYNAME env EGG "\\x90"*1000 3250 """ 3251 # set options 3252 def _set_option(name, value): 3253 if name in config.Option.options: 3254 config.Option.set(name, value) 3255 msg("%s = %s" % (name, repr(value))) 3256 else: 3257 msg("Unknown option: %s" % name) 3258 return 3259 3260 # set args 3261 def _set_arg(*arg): 3262 cmd = "set args" 3263 for a in arg: 3264 try: 3265 s = eval('%s' % a) 3266 if isinstance(s, six.integer_types + six.string_types): 3267 a = s 3268 except: 3269 pass 3270 cmd += " '%s'" % a 3271 peda.execute(cmd) 3272 return 3273 3274 # set env 3275 def _set_env(name, value): 3276 env = peda.execute_redirect("show env") 3277 cmd = "set env %s " % name 3278 try: 3279 value = eval('%s' % value) 3280 except: 3281 pass 3282 cmd += '%s' % value 3283 peda.execute(cmd) 3284 3285 return 3286 3287 (opt, name, value) = normalize_argv(arg, 3) 3288 if opt is None: 3289 self._missing_argument() 3290 3291 if opt.startswith("opt"): 3292 if value is None: 3293 self._missing_argument() 3294 _set_option(name, value) 3295 elif opt.startswith("arg"): 3296 _set_arg(*arg[1:]) 3297 elif opt.startswith("env"): 3298 _set_env(name, value) 3299 else: 3300 msg("Unknown set option: %s" % known_args.opt) 3301 return 3302 set.options = ["option", "arg", "env"] 3303 3304 def hexprint(self, *arg): 3305 """ 3306 Display hexified of data in memory 3307 Usage: 3308 MYNAME address (display 16 bytes from address) 3309 MYNAME address count 3310 MYNAME address /count (display "count" lines, 16-bytes each) 3311 """ 3312 (address, count) = normalize_argv(arg, 2) 3313 if address is None: 3314 self._missing_argument() 3315 3316 if count is None: 3317 count = 16 3318 3319 if not to_int(count) and count.startswith("/"): 3320 count = to_int(count[1:]) 3321 count = count * 16 if count else None 3322 3323 bytes_ = peda.dumpmem(address, address+count) 3324 if bytes_ is None: 3325 warning_msg("cannot retrieve memory content") 3326 else: 3327 hexstr = to_hexstr(bytes_) 3328 linelen = 16 # display 16-bytes per line 3329 i = 0 3330 text = "" 3331 while hexstr: 3332 text += '%s : "%s"\n' % (blue(to_address(address+i*linelen)), hexstr[:linelen*4]) 3333 hexstr = hexstr[linelen*4:] 3334 i += 1 3335 pager(text) 3336 3337 return 3338 3339 def hexdump(self, *arg): 3340 """ 3341 Display hex/ascii dump of data in memory 3342 Usage: 3343 MYNAME address (dump 16 bytes from address) 3344 MYNAME address count 3345 MYNAME address /count (dump "count" lines, 16-bytes each) 3346 """ 3347 def ascii_char(ch): 3348 if ord(ch) >= 0x20 and ord(ch) < 0x7e: 3349 return chr(ord(ch)) # Ensure we return a str 3350 else: 3351 return "." 3352 3353 (address, count) = normalize_argv(arg, 2) 3354 if address is None: 3355 self._missing_argument() 3356 3357 if count is None: 3358 count = 16 3359 3360 if not to_int(count) and count.startswith("/"): 3361 count = to_int(count[1:]) 3362 count = count * 16 if count else None 3363 3364 bytes_ = peda.dumpmem(address, address+count) 3365 if bytes_ is None: 3366 warning_msg("cannot retrieve memory content") 3367 else: 3368 linelen = 16 # display 16-bytes per line 3369 i = 0 3370 text = "" 3371 while bytes_: 3372 buf = bytes_[:linelen] 3373 hexbytes = " ".join(["%02x" % ord(c) for c in bytes_iterator(buf)]) 3374 asciibytes = "".join([ascii_char(c) for c in bytes_iterator(buf)]) 3375 text += '%s : %s %s\n' % (blue(to_address(address+i*linelen)), hexbytes.ljust(linelen*3), asciibytes) 3376 bytes_ = bytes_[linelen:] 3377 i += 1 3378 pager(text) 3379 3380 return 3381 3382 def aslr(self, *arg): 3383 """ 3384 Show/set ASLR setting of GDB 3385 Usage: 3386 MYNAME [on|off] 3387 """ 3388 (option,) = normalize_argv(arg, 1) 3389 if option is None: 3390 out = peda.execute_redirect("show disable-randomization") 3391 if not out: 3392 warning_msg("ASLR setting is unknown or not available") 3393 return 3394 3395 if "is off" in out: 3396 msg("ASLR is %s" % green("ON")) 3397 if "is on" in out: 3398 msg("ASLR is %s" % red("OFF")) 3399 else: 3400 option = option.strip().lower() 3401 if option in ["on", "off"]: 3402 peda.execute("set disable-randomization %s" % ("off" if option == "on" else "on")) 3403 3404 return 3405 3406 def xprint(self, *arg): 3407 """ 3408 Extra support to GDB's print command 3409 Usage: 3410 MYNAME expression 3411 """ 3412 text = "" 3413 exp = " ".join(list(arg)) 3414 m = re.search(".*\[(.*)\]|.*?s:(0x[^ ]*)", exp) 3415 if m: 3416 addr = peda.parse_and_eval(m.group(1)) 3417 if to_int(addr): 3418 text += "[0x%x]: " % to_int(addr) 3419 3420 out = peda.parse_and_eval(exp) 3421 if to_int(out): 3422 chain = peda.examine_mem_reference(to_int(out)) 3423 text += format_reference_chain(chain) 3424 msg(text) 3425 return 3426 3427 def distance(self, *arg): 3428 """ 3429 Calculate distance between two addresses 3430 Usage: 3431 MYNAME address (calculate from current $SP to address) 3432 MYNAME address1 address2 3433 """ 3434 (start, end) = normalize_argv(arg, 2) 3435 if to_int(start) is None or (to_int(end) is None and not self._is_running()): 3436 self._missing_argument() 3437 3438 sp = None 3439 if end is None: 3440 sp = peda.getreg("sp") 3441 end = start 3442 start = sp 3443 3444 dist = end - start 3445 text = "From 0x%x%s to 0x%x: " % (start, " (SP)" if start == sp else "", end) 3446 text += "%d bytes, %d dwords%s" % (dist, dist//4, " (+%d bytes)" % (dist%4) if (dist%4 != 0) else "") 3447 msg(text) 3448 3449 return 3450 3451 def session(self, *arg): 3452 """ 3453 Save/restore a working gdb session to file as a script 3454 Usage: 3455 MYNAME save [filename] 3456 MYNAME restore [filename] 3457 """ 3458 options = ["save", "restore", "autosave"] 3459 (option, filename) = normalize_argv(arg, 2) 3460 if option not in options: 3461 self._missing_argument() 3462 3463 if not filename: 3464 filename = peda.get_config_filename("session") 3465 3466 if option == "save": 3467 if peda.save_session(filename): 3468 msg("Saved GDB session to file %s" % filename) 3469 else: 3470 msg("Failed to save GDB session") 3471 3472 if option == "restore": 3473 if peda.restore_session(filename): 3474 msg("Restored GDB session from file %s" % filename) 3475 else: 3476 msg("Failed to restore GDB session") 3477 3478 if option == "autosave": 3479 if config.Option.get("autosave") == "on": 3480 peda.save_session(filename) 3481 3482 return 3483 session.options = ["save", "restore"] 3484 3485 ################################# 3486 # Debugging Helper Commands # 3487 ################################# 3488 def procinfo(self, *arg): 3489 """ 3490 Display various info from /proc/pid/ 3491 Usage: 3492 MYNAME [pid] 3493 """ 3494 options = ["exe", "fd", "pid", "ppid", "uid", "gid"] 3495 3496 if peda.getos() != "Linux": 3497 warning_msg("this command is only available on Linux") 3498 3499 (pid,) = normalize_argv(arg, 1) 3500 3501 if not pid: 3502 pid = peda.getpid() 3503 3504 if not pid: 3505 return 3506 3507 info = {} 3508 try: 3509 info["exe"] = os.path.realpath("/proc/%d/exe" % pid) 3510 except: 3511 warning_msg("cannot access /proc/%d/" % pid) 3512 return 3513 3514 # fd list 3515 info["fd"] = {} 3516 fdlist = os.listdir("/proc/%d/fd" % pid) 3517 for fd in fdlist: 3518 rpath = os.readlink("/proc/%d/fd/%s" % (pid, fd)) 3519 sock = re.search("socket:\[(.*)\]", rpath) 3520 if sock: 3521 spath = execute_external_command("netstat -aen | grep %s" % sock.group(1)) 3522 if spath: 3523 rpath = spath.strip() 3524 info["fd"][to_int(fd)] = rpath 3525 3526 # uid/gid, pid, ppid 3527 info["pid"] = pid 3528 status = open("/proc/%d/status" % pid).read() 3529 ppid = re.search("PPid:\s*([^\s]*)", status).group(1) 3530 info["ppid"] = to_int(ppid) if ppid else -1 3531 uid = re.search("Uid:\s*([^\n]*)", status).group(1) 3532 info["uid"] = [to_int(id) for id in uid.split()] 3533 gid = re.search("Gid:\s*([^\n]*)", status).group(1) 3534 info["gid"] = [to_int(id) for id in gid.split()] 3535 3536 for opt in options: 3537 if opt == "fd": 3538 for (fd, path) in info[opt].items(): 3539 msg("fd[%d] -> %s" % (fd, path)) 3540 else: 3541 msg("%s = %s" % (opt, info[opt])) 3542 return 3543 3544 # getfile() 3545 def getfile(self): 3546 """ 3547 Get exec filename of current debugged process 3548 Usage: 3549 MYNAME 3550 """ 3551 filename = peda.getfile() 3552 if filename == None: 3553 msg("No file specified") 3554 else: 3555 msg(filename) 3556 return 3557 3558 # getpid() 3559 def getpid(self): 3560 """ 3561 Get PID of current debugged process 3562 Usage: 3563 MYNAME 3564 """ 3565 pid = self._is_running() 3566 msg(pid) 3567 return 3568 3569 # disassemble() 3570 def pdisass(self, *arg): 3571 """ 3572 Format output of gdb disassemble command with colors 3573 Usage: 3574 MYNAME "args for gdb disassemble command" 3575 MYNAME address /NN: equivalent to "x/NNi address" 3576 """ 3577 (address, fmt_count) = normalize_argv(arg, 2) 3578 if isinstance(fmt_count, str) and fmt_count.startswith("/"): 3579 count = to_int(fmt_count[1:]) 3580 if not count or to_int(address) is None: 3581 self._missing_argument() 3582 else: 3583 code = peda.get_disasm(address, count) 3584 else: 3585 code = peda.disassemble(*arg) 3586 msg(format_disasm_code(code)) 3587 3588 return 3589 3590 # disassemble_around 3591 def nearpc(self, *arg): 3592 """ 3593 Disassemble instructions nearby current PC or given address 3594 Usage: 3595 MYNAME [count] 3596 MYNAME address [count] 3597 count is maximum 256 3598 """ 3599 (address, count) = normalize_argv(arg, 2) 3600 address = to_int(address) 3601 3602 count = to_int(count) 3603 if address is not None and address < 0x40000: 3604 count = address 3605 address = None 3606 3607 if address is None: 3608 address = peda.getreg("pc") 3609 3610 if count is None: 3611 code = peda.disassemble_around(address) 3612 else: 3613 code = peda.disassemble_around(address, count) 3614 3615 if code: 3616 msg(format_disasm_code(code, address)) 3617 else: 3618 error_msg("invalid $pc address or instruction count") 3619 return 3620 3621 def waitfor(self, *arg): 3622 """ 3623 Try to attach to new forked process; mimic "attach -waitfor" 3624 Usage: 3625 MYNAME [cmdname] 3626 MYNAME [cmdname] -c (auto continue after attached) 3627 """ 3628 (name, opt) = normalize_argv(arg, 2) 3629 if name == "-c": 3630 opt = name 3631 name = None 3632 3633 if name is None: 3634 filename = peda.getfile() 3635 if filename is None: 3636 warning_msg("please specify the file to debug or process name to attach") 3637 return 3638 else: 3639 name = os.path.basename(filename) 3640 3641 msg("Trying to attach to new forked process (%s), Ctrl-C to stop..." % name) 3642 cmd = "ps axo pid,command | grep %s | grep -v grep" % name 3643 getpids = [] 3644 out = execute_external_command(cmd) 3645 for line in out.splitlines(): 3646 getpids += [line.split()[0].strip()] 3647 3648 while True: 3649 found = 0 3650 out = execute_external_command(cmd) 3651 for line in out.splitlines(): 3652 line = line.split() 3653 pid = line[0].strip() 3654 cmdname = line[1].strip() 3655 if name not in cmdname: continue 3656 if pid not in getpids: 3657 found = 1 3658 break 3659 3660 if found == 1: 3661 msg("Attching to pid: %s, cmdname: %s" % (pid, cmdname)) 3662 if peda.getpid(): 3663 peda.execute("detach") 3664 out = peda.execute_redirect("attach %s" % pid) 3665 msg(out) 3666 out = peda.execute_redirect("file %s" % cmdname) # reload symbol file 3667 msg(out) 3668 if opt == "-c": 3669 peda.execute("continue") 3670 return 3671 time.sleep(0.5) 3672 return 3673 3674 def pltbreak(self, *arg): 3675 """ 3676 Set breakpoint at PLT functions match name regex 3677 Usage: 3678 MYNAME [name] 3679 """ 3680 (name,) = normalize_argv(arg, 1) 3681 if not name: 3682 name = "" 3683 headers = peda.elfheader() 3684 end = headers[".bss"] 3685 symbols = peda.elfsymbol(name) 3686 if len(symbols) == 0: 3687 msg("File not specified or PLT symbols not found") 3688 return 3689 else: 3690 # Traverse symbols in order to have more predictable output 3691 for symname in sorted(symbols): 3692 if "plt" not in symname: continue 3693 if name in symname: # fixme(longld) bounds checking? 3694 line = peda.execute_redirect("break %s" % symname) 3695 msg("%s (%s)" % (line.strip("\n"), symname)) 3696 return 3697 3698 def xrefs(self, *arg): 3699 """ 3700 Search for all call/data access references to a function/variable 3701 Usage: 3702 MYNAME pattern 3703 MYNAME pattern file/mapname 3704 """ 3705 (search, filename) = normalize_argv(arg, 2) 3706 if search is None: 3707 search = "" # search for all call references 3708 else: 3709 search = arg[0] 3710 3711 if filename is not None: # get full path to file if mapname is provided 3712 vmap = peda.get_vmmap(filename) 3713 if vmap: 3714 filename = vmap[0][3] 3715 3716 result = peda.xrefs(search, filename) 3717 if result: 3718 if search != "": 3719 msg("All references to '%s':" % search) 3720 else: 3721 msg("All call references") 3722 for (addr, code) in result: 3723 msg("%s" % (code)) 3724 else: 3725 msg("Not found") 3726 return 3727 3728 def deactive(self, *arg): 3729 """ 3730 Bypass a function by ignoring its execution (eg sleep/alarm) 3731 Usage: 3732 MYNAME function 3733 MYNAME function del (re-active) 3734 """ 3735 (function, action) = normalize_argv(arg, 2) 3736 if function is None: 3737 self._missing_argument() 3738 3739 if to_int(function): 3740 function = "0x%x" % function 3741 3742 bnum = "$deactive_%s_bnum" % function 3743 if action and "del" in action: 3744 peda.execute("delete %s" % bnum) 3745 peda.execute("set %s = \"void\"" % bnum) 3746 msg("'%s' re-activated" % function) 3747 return 3748 3749 if "void" not in peda.execute_redirect("p %s" % bnum): 3750 out = peda.execute_redirect("info breakpoints %s" % bnum) 3751 if out: 3752 msg("Already deactivated '%s'" % function) 3753 msg(out) 3754 return 3755 else: 3756 peda.execute("set %s = \"void\"" % bnum) 3757 3758 (arch, bits) = peda.getarch() 3759 if not function.startswith("0x"): # named function 3760 symbol = peda.elfsymbol(function) 3761 if not symbol: 3762 warning_msg("cannot retrieve info of function '%s'" % function) 3763 return 3764 peda.execute("break *0x%x" % symbol[function + "@plt"]) 3765 3766 else: # addressed function 3767 peda.execute("break *%s" % function) 3768 3769 peda.execute("set %s = $bpnum" % bnum) 3770 tmpfd = tmpfile() 3771 if "i386" in arch: 3772 tmpfd.write("\n".join([ 3773 "commands $bpnum", 3774 "silent", 3775 "set $eax = 0", 3776 "return", 3777 "continue", 3778 "end"])) 3779 if "64" in arch: 3780 tmpfd.write("\n".join([ 3781 "commands $bpnum", 3782 "silent", 3783 "set $rax = 0", 3784 "return", 3785 "continue", 3786 "end"])) 3787 tmpfd.flush() 3788 peda.execute("source %s" % tmpfd.name) 3789 tmpfd.close() 3790 out = peda.execute_redirect("info breakpoints %s" % bnum) 3791 if out: 3792 msg("'%s' deactivated" % function) 3793 msg(out) 3794 return 3795 3796 def unptrace(self, *arg): 3797 """ 3798 Disable anti-ptrace detection 3799 Usage: 3800 MYNAME 3801 MYNAME del 3802 """ 3803 (action,) = normalize_argv(arg, 1) 3804 3805 self.deactive("ptrace", action) 3806 3807 if not action and "void" in peda.execute_redirect("p $deactive_ptrace_bnum"): 3808 # cannot deactive vi plt entry, try syscall method 3809 msg("Try to patch 'ptrace' via syscall") 3810 peda.execute("catch syscall ptrace") 3811 peda.execute("set $deactive_ptrace_bnum = $bpnum") 3812 tmpfd = tmpfile() 3813 (arch, bits) = peda.getarch() 3814 if "i386" in arch: 3815 tmpfd.write("\n".join([ 3816 "commands $bpnum", 3817 "silent", 3818 "if (*(int*)($esp+4) == 0 || $ebx == 0)", 3819 " set $eax = 0", 3820 "end", 3821 "continue", 3822 "end"])) 3823 if "64" in arch: 3824 tmpfd.write("\n".join([ 3825 "commands $bpnum", 3826 "silent", 3827 "if ($rdi == 0)", 3828 " set $rax = 0", 3829 "end", 3830 "continue", 3831 "end"])) 3832 tmpfd.flush() 3833 peda.execute("source %s" % tmpfd.name) 3834 tmpfd.close() 3835 out = peda.execute_redirect("info breakpoints $deactive_ptrace_bnum") 3836 if out: 3837 msg("'ptrace' deactivated") 3838 msg(out) 3839 return 3840 3841 # get_function_args() 3842 def dumpargs(self, *arg): 3843 """ 3844 Display arguments passed to a function when stopped at a call instruction 3845 Usage: 3846 MYNAME [count] 3847 count: force to display "count args" instead of guessing 3848 """ 3849 3850 (count,) = normalize_argv(arg, 1) 3851 if not self._is_running(): 3852 return 3853 3854 args = peda.get_function_args(count) 3855 if args: 3856 msg("Guessed arguments:") 3857 for (i, a) in enumerate(args): 3858 chain = peda.examine_mem_reference(a) 3859 msg("arg[%d]: %s" % (i, format_reference_chain(chain))) 3860 else: 3861 msg("No argument") 3862 3863 return 3864 3865 def xuntil(self, *arg): 3866 """ 3867 Continue execution until an address or function 3868 Usage: 3869 MYNAME address | function 3870 """ 3871 (address,) = normalize_argv(arg, 1) 3872 if to_int(address) is None: 3873 peda.execute("tbreak %s" % address) 3874 else: 3875 peda.execute("tbreak *0x%x" % address) 3876 pc = peda.getreg("pc") 3877 if pc is None: 3878 peda.execute("run") 3879 else: 3880 peda.execute("continue") 3881 return 3882 3883 def goto(self, *arg): 3884 """ 3885 Continue execution at an address 3886 Usage: 3887 MYNAME address 3888 """ 3889 (address,) = normalize_argv(arg, 1) 3890 if to_int(address) is None: 3891 self._missing_argument() 3892 3893 peda.execute("set $pc = 0x%x" % address) 3894 peda.execute("stop") 3895 return 3896 3897 def skipi(self, *arg): 3898 """ 3899 Skip execution of next count instructions 3900 Usage: 3901 MYNAME [count] 3902 """ 3903 (count,) = normalize_argv(arg, 1) 3904 if to_int(count) is None: 3905 count = 1 3906 3907 if not self._is_running(): 3908 return 3909 3910 next_code = peda.next_inst(peda.getreg("pc"), count) 3911 if not next_code: 3912 warning_msg("failed to get next instructions") 3913 return 3914 last_addr = next_code[-1][0] 3915 peda.execute("set $pc = 0x%x" % last_addr) 3916 peda.execute("stop") 3917 return 3918 3919 def start(self, *arg): 3920 """ 3921 Start debugged program and stop at most convenient entry 3922 Usage: 3923 MYNAME 3924 """ 3925 entries = ["main"] 3926 main_addr = peda.main_entry() 3927 if main_addr: 3928 entries += ["*0x%x" % main_addr] 3929 entries += ["__libc_start_main@plt"] 3930 entries += ["_start"] 3931 entries += ["_init"] 3932 3933 started = 0 3934 for e in entries: 3935 out = peda.execute_redirect("tbreak %s" % e) 3936 if out and "breakpoint" in out: 3937 peda.execute("run %s" % ' '.join(arg)) 3938 started = 1 3939 break 3940 3941 if not started: # try ELF entry point or just "run" as the last resort 3942 elf_entry = peda.elfentry() 3943 if elf_entry: 3944 out = peda.execute_redirect("tbreak *%s" % elf_entry) 3945 3946 peda.execute("run") 3947 3948 return 3949 3950 # stepuntil() 3951 def stepuntil(self, *arg): 3952 """ 3953 Step until a desired instruction in specific memory range 3954 Usage: 3955 MYNAME "inst1,inst2" (step to next inst in binary) 3956 MYNAME "inst1,inst2" mapname1,mapname2 3957 """ 3958 (insts, mapname) = normalize_argv(arg, 2) 3959 if insts is None: 3960 self._missing_argument() 3961 3962 if not self._is_running(): 3963 return 3964 3965 peda.save_user_command("hook-stop") # disable hook-stop to speedup 3966 msg("Stepping through, Ctrl-C to stop...") 3967 result = peda.stepuntil(insts, mapname) 3968 peda.restore_user_command("hook-stop") 3969 3970 if result: 3971 peda.execute("stop") 3972 return 3973 3974 # wrapper for stepuntil("call") 3975 def nextcall(self, *arg): 3976 """ 3977 Step until next 'call' instruction in specific memory range 3978 Usage: 3979 MYNAME [keyword] [mapname1,mapname2] 3980 """ 3981 (keyword, mapname) = normalize_argv(arg, 2) 3982 3983 if keyword: 3984 self.stepuntil("call.*%s" % keyword, mapname) 3985 else: 3986 self.stepuntil("call", mapname) 3987 return 3988 3989 # wrapper for stepuntil("j") 3990 def nextjmp(self, *arg): 3991 """ 3992 Step until next 'j*' instruction in specific memory range 3993 Usage: 3994 MYNAME [keyword] [mapname1,mapname2] 3995 """ 3996 (keyword, mapname) = normalize_argv(arg, 2) 3997 3998 if keyword: 3999 self.stepuntil("j.*%s" % keyword, mapname) 4000 else: 4001 self.stepuntil("j", mapname) 4002 return 4003 4004 #stepuntil() 4005 def tracecall(self, *arg): 4006 """ 4007 Trace function calls made by the program 4008 Usage: 4009 MYNAME ["func1,func2"] [mapname1,mapname2] 4010 MYNAME ["-func1,func2"] [mapname1,mapname2] (inverse) 4011 default is to trace internal calls made by the program 4012 """ 4013 (funcs, mapname) = normalize_argv(arg, 2) 4014 4015 if not self._is_running(): 4016 return 4017 4018 if not mapname: 4019 mapname = "binary" 4020 4021 fnames = [""] 4022 if funcs: 4023 if to_int(funcs): 4024 funcs = "0x%x" % funcs 4025 fnames = funcs.replace(",", " ").split() 4026 for (idx, fn) in enumerate(fnames): 4027 if to_int(fn): 4028 fnames[idx] = "0x%x" % to_int(fn) 4029 4030 inverse = 0 4031 for (idx, fn) in enumerate(fnames): 4032 if fn.startswith("-"): # inverse trace 4033 fnames[idx] = fn[1:] 4034 inverse = 1 4035 4036 binname = peda.getfile() 4037 logname = peda.get_config_filename("tracelog") 4038 4039 if mapname is None: 4040 mapname = binname 4041 4042 peda.save_user_command("hook-stop") # disable hook-stop to speedup 4043 msg("Tracing calls %s '%s', Ctrl-C to stop..." % ("match" if not inverse else "not match", ",".join(fnames))) 4044 prev_depth = peda.backtrace_depth(peda.getreg("sp")) 4045 4046 logfd = open(logname, "w") 4047 while True: 4048 result = peda.stepuntil("call", mapname, prev_depth) 4049 if result is None: 4050 break 4051 (call_depth, code) = result 4052 prev_depth += call_depth 4053 if not code.startswith("=>"): 4054 break 4055 4056 if not inverse: 4057 matched = False 4058 for fn in fnames: 4059 fn = fn.strip() 4060 if re.search(fn, code.split(":\t")[-1]): 4061 matched = True 4062 break 4063 else: 4064 matched = True 4065 for fn in fnames: 4066 fn = fn.strip() 4067 if re.search(fn, code.split(":\t")[-1]): 4068 matched = False 4069 break 4070 4071 if matched: 4072 code = format_disasm_code(code) 4073 msg("%s%s%s" % (" "*(prev_depth-1), " dep:%02d " % (prev_depth-1), colorize(code.strip())), teefd=logfd) 4074 args = peda.get_function_args() 4075 if args: 4076 for (i, a) in enumerate(args): 4077 chain = peda.examine_mem_reference(a) 4078 text = "%s |-- arg[%d]: %s" % (" "*(prev_depth-1), i, format_reference_chain(chain)) 4079 msg(text, teefd=logfd) 4080 4081 msg(code, "red") 4082 peda.restore_user_command("hook-stop") 4083 if "STOP" not in peda.get_status(): 4084 peda.execute("stop") 4085 logfd.close() 4086 msg("Saved trace information in file %s, view with 'less -r file'" % logname) 4087 return 4088 4089 # stepuntil() 4090 def traceinst(self, *arg): 4091 """ 4092 Trace specific instructions executed by the program 4093 Usage: 4094 MYNAME ["inst1,inst2"] [mapname1,mapname2] 4095 MYNAME count (trace execution of next count instrcutions) 4096 default is to trace instructions inside the program 4097 """ 4098 (insts, mapname) = normalize_argv(arg, 2) 4099 4100 if not self._is_running(): 4101 return 4102 4103 if not mapname: 4104 mapname = "binary" 4105 4106 instlist = [".*"] 4107 count = -1 4108 if insts: 4109 if to_int(insts): 4110 count = insts 4111 else: 4112 instlist = insts.replace(",", " ").split() 4113 4114 binname = peda.getfile() 4115 logname = peda.get_config_filename("tracelog") 4116 4117 if mapname is None: 4118 mapname = binname 4119 4120 peda.save_user_command("hook-stop") # disable hook-stop to speedup 4121 msg("Tracing instructions match '%s', Ctrl-C to stop..." % (",".join(instlist))) 4122 prev_depth = peda.backtrace_depth(peda.getreg("sp")) 4123 logfd = open(logname, "w") 4124 4125 p = re.compile(".*?:\s*[^ ]*\s*([^,]*),(.*)") 4126 while count: 4127 result = peda.stepuntil(",".join(instlist), mapname, prev_depth) 4128 if result is None: 4129 break 4130 (call_depth, code) = result 4131 prev_depth += call_depth 4132 if not code.startswith("=>"): 4133 break 4134 4135 # special case for JUMP inst 4136 prev_code = "" 4137 if re.search("j[^m]", code.split(":\t")[-1].split()[0]): 4138 prev_insts = peda.prev_inst(peda.getreg("pc")) 4139 if prev_insts: 4140 prev_code = "0x%x:%s" % prev_insts[0] 4141 msg("%s%s%s" % (" "*(prev_depth-1), " dep:%02d " % (prev_depth-1), prev_code), teefd=logfd) 4142 4143 text = "%s%s%s" % (" "*(prev_depth-1), " dep:%02d " % (prev_depth-1), code.strip()) 4144 msg(text, teefd=logfd) 4145 4146 if re.search("call", code.split(":\t")[-1].split()[0]): 4147 args = peda.get_function_args() 4148 if args: 4149 for (i, a) in enumerate(args): 4150 chain = peda.examine_mem_reference(a) 4151 text = "%s |-- arg[%d]: %s" % (" "*(prev_depth-1), i, format_reference_chain(chain)) 4152 msg(text, teefd=logfd) 4153 4154 # get registers info if any 4155 (arch, bits) = peda.getarch() 4156 code = code.split("#")[0].strip("=>") 4157 if prev_code: 4158 m = p.search(prev_code) 4159 else: 4160 m = p.search(code) 4161 4162 if m: 4163 for op in m.groups(): 4164 if op.startswith("0x"): continue 4165 v = to_int(peda.parse_and_eval(op)) 4166 chain = peda.examine_mem_reference(v) 4167 text = "%s |-- %03s: %s" % (" "*(prev_depth-1), op, format_reference_chain(chain)) 4168 msg(text, teefd=logfd) 4169 4170 count -= 1 4171 4172 msg(code, "red") 4173 peda.restore_user_command("hook-stop") 4174 logfd.close() 4175 msg("Saved trace information in file %s, view with 'less -r file'" % logname) 4176 return 4177 4178 def profile(self, *arg): 4179 """ 4180 Simple profiling to count executed instructions in the program 4181 Usage: 4182 MYNAME count [keyword] 4183 default is to count instructions inside the program only 4184 count = 0: run until end of execution 4185 keyword: only display stats for instructions matched it 4186 """ 4187 (count, keyword) = normalize_argv(arg, 2) 4188 4189 if count is None: 4190 self._missing_argument() 4191 4192 if not self._is_running(): 4193 return 4194 4195 if keyword is None or keyword == "all": 4196 keyword = "" 4197 4198 keyword = keyword.replace(" ", "").split(",") 4199 4200 peda.save_user_command("hook-stop") # disable hook-stop to speedup 4201 msg("Stepping %s instructions, Ctrl-C to stop..." % ("%d" % count if count else "all")) 4202 4203 if count == 0: 4204 count = -1 4205 stats = {} 4206 total = 0 4207 binmap = peda.get_vmmap("binary") 4208 try: 4209 while count != 0: 4210 pc = peda.getreg("pc") 4211 if not peda.is_address(pc): 4212 break 4213 code = peda.get_disasm(pc) 4214 if not code: 4215 break 4216 if peda.is_address(pc, binmap): 4217 for k in keyword: 4218 if k in code.split(":\t")[-1]: 4219 code = code.strip("=>").strip() 4220 stats.setdefault(code, 0) 4221 stats[code] += 1 4222 break 4223 peda.execute_redirect("stepi", silent=True) 4224 else: 4225 peda.execute_redirect("stepi", silent=True) 4226 peda.execute_redirect("finish", silent=True) 4227 count -= 1 4228 total += 1 4229 except: 4230 pass 4231 4232 peda.restore_user_command("hook-stop") 4233 text = "Executed %d instructions\n" % total 4234 text += "%s %s\n" % (blue("Run-count", "bold"), blue("Instruction", "bold")) 4235 for (code, count) in sorted(stats.items(), key = lambda x: x[1], reverse=True): 4236 text += "%8d: %s\n" % (count, code) 4237 pager(text) 4238 4239 return 4240 4241 @msg.bufferize 4242 def context_register(self, *arg): 4243 """ 4244 Display register information of current execution context 4245 Usage: 4246 MYNAME 4247 """ 4248 if not self._is_running(): 4249 return 4250 4251 pc = peda.getreg("pc") 4252 # display register info 4253 msg("[%s]" % "registers".center(78, "-"), "blue") 4254 self.xinfo("register") 4255 4256 return 4257 4258 @msg.bufferize 4259 def context_code(self, *arg): 4260 """ 4261 Display nearby disassembly at $PC of current execution context 4262 Usage: 4263 MYNAME [linecount] 4264 """ 4265 (count,) = normalize_argv(arg, 1) 4266 4267 if count is None: 4268 count = 8 4269 4270 if not self._is_running(): 4271 return 4272 4273 pc = peda.getreg("pc") 4274 if peda.is_address(pc): 4275 inst = peda.get_disasm(pc) 4276 else: 4277 inst = None 4278 4279 text = blue("[%s]" % "code".center(78, "-")) 4280 msg(text) 4281 if inst: # valid $PC 4282 text = "" 4283 opcode = inst.split(":\t")[-1].split()[0] 4284 # stopped at function call 4285 if "call" in opcode: 4286 text += peda.disassemble_around(pc, count) 4287 msg(format_disasm_code(text, pc)) 4288 self.dumpargs() 4289 # stopped at jump 4290 elif "j" in opcode: 4291 jumpto = peda.testjump(inst) 4292 if jumpto: # JUMP is taken 4293 code = peda.disassemble_around(pc, count) 4294 code = code.splitlines() 4295 pc_idx = 999 4296 for (idx, line) in enumerate(code): 4297 if ("0x%x" % pc) in line.split(":")[0]: 4298 pc_idx = idx 4299 if idx <= pc_idx: 4300 text += line + "\n" 4301 else: 4302 text += " | %s\n" % line.strip() 4303 text = format_disasm_code(text, pc) + "\n" 4304 text += " |->" 4305 code = peda.get_disasm(jumpto, count//2) 4306 if not code: 4307 code = " Cannot evaluate jump destination\n" 4308 4309 code = code.splitlines() 4310 text += red(code[0]) + "\n" 4311 for line in code[1:]: 4312 text += " %s\n" % line.strip() 4313 text += red("JUMP is taken".rjust(79)) 4314 else: # JUMP is NOT taken 4315 text += format_disasm_code(peda.disassemble_around(pc, count), pc) 4316 text += "\n" + green("JUMP is NOT taken".rjust(79)) 4317 4318 msg(text.rstrip()) 4319 # stopped at other instructions 4320 else: 4321 text += peda.disassemble_around(pc, count) 4322 msg(format_disasm_code(text, pc)) 4323 else: # invalid $PC 4324 msg("Invalid $PC address: 0x%x" % pc, "red") 4325 4326 return 4327 4328 @msg.bufferize 4329 def context_stack(self, *arg): 4330 """ 4331 Display stack of current execution context 4332 Usage: 4333 MYNAME [linecount] 4334 """ 4335 (count,) = normalize_argv(arg, 1) 4336 4337 if not self._is_running(): 4338 return 4339 4340 text = blue("[%s]" % "stack".center(78, "-")) 4341 msg(text) 4342 sp = peda.getreg("sp") 4343 if peda.is_address(sp): 4344 self.telescope(sp, count) 4345 else: 4346 msg("Invalid $SP address: 0x%x" % sp, "red") 4347 4348 return 4349 4350 def context(self, *arg): 4351 """ 4352 Display various information of current execution context 4353 Usage: 4354 MYNAME [reg,code,stack,all] [code/stack length] 4355 """ 4356 4357 (opt, count) = normalize_argv(arg, 2) 4358 4359 if to_int(count) is None: 4360 count = 8 4361 if opt is None: 4362 opt = config.Option.get("context") 4363 if opt == "all": 4364 opt = "register,code,stack" 4365 4366 opt = opt.replace(" ", "").split(",") 4367 4368 if not opt: 4369 return 4370 4371 if not self._is_running(): 4372 return 4373 4374 clearscr = config.Option.get("clearscr") 4375 if clearscr == "on": 4376 clearscreen() 4377 4378 status = peda.get_status() 4379 # display registers 4380 if "reg" in opt or "register" in opt: 4381 self.context_register() 4382 4383 # display assembly code 4384 if "code" in opt: 4385 self.context_code(count) 4386 4387 # display stack content, forced in case SIGSEGV 4388 if "stack" in opt or "SIGSEGV" in status: 4389 self.context_stack(count) 4390 msg("[%s]" % ("-"*78), "blue") 4391 msg("Legend: %s, %s, %s, value" % (red("code"), blue("data"), green("rodata"))) 4392 4393 # display stopped reason 4394 if "SIG" in status: 4395 msg("Stopped reason: %s" % red(status)) 4396 4397 return 4398 4399 def breakrva(self, *arg): 4400 """ 4401 Set breakpoint by Relative Virtual Address (RVA) 4402 Usage: 4403 MYNAME rva 4404 MYNAME rva module_name (e.g binary, shared module name) 4405 """ 4406 (rva, module) = normalize_argv(arg, 2) 4407 if rva is None or not to_int(rva): 4408 self._missing_argument() 4409 if module is None: 4410 module = 'binary' 4411 4412 binmap = peda.get_vmmap(module) 4413 if len(binmap) == 0: 4414 msg("No module matches '%s'" % module) 4415 else: 4416 base_address = binmap[0][0] 4417 peda.set_breakpoint(base_address+rva) 4418 return 4419 4420 ################################# 4421 # Memory Operation Commands # 4422 ################################# 4423 # get_vmmap() 4424 def vmmap(self, *arg): 4425 """ 4426 Get virtual mapping address ranges of section(s) in debugged process 4427 Usage: 4428 MYNAME [mapname] (e.g binary, all, libc, stack) 4429 MYNAME address (find mapname contains this address) 4430 MYNAME (equiv to cat /proc/pid/maps) 4431 """ 4432 4433 (mapname,) = normalize_argv(arg, 1) 4434 if not self._is_running(): 4435 maps = peda.get_vmmap() 4436 elif to_int(mapname) is None: 4437 maps = peda.get_vmmap(mapname) 4438 else: 4439 addr = to_int(mapname) 4440 maps = [] 4441 allmaps = peda.get_vmmap() 4442 if allmaps is not None: 4443 for (start, end, perm, name) in allmaps: 4444 if addr >= start and addr < end: 4445 maps += [(start, end, perm, name)] 4446 4447 if maps is not None and len(maps) > 0: 4448 l = 10 if peda.intsize() == 4 else 18 4449 msg("%s %s %s\t%s" % ("Start".ljust(l, " "), "End".ljust(l, " "), "Perm", "Name"), "blue", "bold") 4450 for (start, end, perm, name) in maps: 4451 color = "red" if "rwx" in perm else None 4452 msg("%s %s %s\t%s" % (to_address(start).ljust(l, " "), to_address(end).ljust(l, " "), perm, name), color) 4453 else: 4454 warning_msg("not found or cannot access procfs") 4455 return 4456 4457 # writemem() 4458 def patch(self, *arg): 4459 """ 4460 Patch memory start at an address with string/hexstring/int 4461 Usage: 4462 MYNAME address (multiple lines input) 4463 MYNAME address "string" 4464 MYNAME from_address to_address "string" 4465 MYNAME (will patch at current $pc) 4466 """ 4467 4468 (address, data, byte) = normalize_argv(arg, 3) 4469 address = to_int(address) 4470 end_address = None 4471 if address is None: 4472 address = peda.getreg("pc") 4473 4474 if byte is not None and to_int(data) is not None: 4475 end_address, data = to_int(data), byte 4476 if end_address < address: 4477 address, end_address = end_address, address 4478 4479 if data is None: 4480 data = "" 4481 while True: 4482 line = input("patch> ") 4483 if line.strip() == "": continue 4484 if line == "end": 4485 break 4486 user_input = line.strip() 4487 if user_input.startswith("0x"): 4488 data += hex2str(user_input) 4489 else: 4490 data += eval("%s" % user_input) 4491 4492 if to_int(data) is not None: 4493 data = hex2str(to_int(data), peda.intsize()) 4494 4495 data = to_binary_string(data) 4496 data = data.replace(b"\\\\", b"\\") 4497 if end_address: 4498 data *= (end_address-address + 1) // len(data) 4499 bytes_ = peda.writemem(address, data) 4500 if bytes_ >= 0: 4501 msg("Written %d bytes to 0x%x" % (bytes_, address)) 4502 else: 4503 warning_msg("Failed to patch memory, try 'set write on' first for offline patching") 4504 return 4505 4506 # dumpmem() 4507 def dumpmem(self, *arg): 4508 """ 4509 Dump content of a memory region to raw binary file 4510 Usage: 4511 MYNAME file start end 4512 MYNAME file mapname 4513 """ 4514 (filename, start, end) = normalize_argv(arg, 3) 4515 if end is not None and to_int(end): 4516 if end < start: 4517 start, end = end, start 4518 ret = peda.execute("dump memory %s 0x%x 0x%x" % (filename, start, end)) 4519 if not ret: 4520 warning_msg("failed to dump memory") 4521 else: 4522 msg("Dumped %d bytes to '%s'" % (end-start, filename)) 4523 elif start is not None: # dump by mapname 4524 maps = peda.get_vmmap(start) 4525 if maps: 4526 fd = open(filename, "wb") 4527 count = 0 4528 for (start, end, _, _) in maps: 4529 mem = peda.dumpmem(start, end) 4530 if mem is None: # nullify unreadable memory 4531 mem = "\x00"*(end-start) 4532 fd.write(mem) 4533 count += end - start 4534 fd.close() 4535 msg("Dumped %d bytes to '%s'" % (count, filename)) 4536 else: 4537 warning_msg("invalid mapname") 4538 else: 4539 self._missing_argument() 4540 4541 return 4542 4543 # loadmem() 4544 def loadmem(self, *arg): 4545 """ 4546 Load contents of a raw binary file to memory 4547 Usage: 4548 MYNAME file address [size] 4549 """ 4550 mem = "" 4551 (filename, address, size) = normalize_argv(arg, 3) 4552 address = to_int(address) 4553 size = to_int(size) 4554 if filename is not None: 4555 try: 4556 mem = open(filename, "rb").read() 4557 except: 4558 pass 4559 if mem == "": 4560 error_msg("cannot read data or filename is empty") 4561 return 4562 if size is not None and size < len(mem): 4563 mem = mem[:size] 4564 bytes = peda.writemem(address, mem) 4565 if bytes > 0: 4566 msg("Written %d bytes to 0x%x" % (bytes, address)) 4567 else: 4568 warning_msg("failed to load filename to memory") 4569 else: 4570 self._missing_argument() 4571 return 4572 4573 # cmpmem() 4574 def cmpmem(self, *arg): 4575 """ 4576 Compare content of a memory region with a file 4577 Usage: 4578 MYNAME start end file 4579 """ 4580 (start, end, filename) = normalize_argv(arg, 3) 4581 if filename is None: 4582 self._missing_argument() 4583 4584 try: 4585 buf = open(filename, "rb").read() 4586 except: 4587 error_msg("cannot read data from filename %s" % filename) 4588 return 4589 4590 result = peda.cmpmem(start, end, buf) 4591 4592 if result is None: 4593 warning_msg("failed to perform comparison") 4594 elif result == {}: 4595 msg("mem and filename are identical") 4596 else: 4597 msg("--- mem: %s -> %s" % (arg[0], arg[1]), "green", "bold") 4598 msg("+++ filename: %s" % arg[2], "blue", "bold") 4599 for (addr, bytes_) in result.items(): 4600 msg("@@ 0x%x @@" % addr, "red") 4601 line_1 = "- " 4602 line_2 = "+ " 4603 for (mem_val, file_val) in bytes_: 4604 m_byte = "%02X " % ord(mem_val) 4605 f_byte = "%02X " % ord(file_val) 4606 if mem_val == file_val: 4607 line_1 += m_byte 4608 line_2 += f_byte 4609 else: 4610 line_1 += green(m_byte) 4611 line_2 += blue(f_byte) 4612 msg(line_1) 4613 msg(line_2) 4614 return 4615 4616 # xormem() 4617 def xormem(self, *arg): 4618 """ 4619 XOR a memory region with a key 4620 Usage: 4621 MYNAME start end key 4622 """ 4623 (start, end, key) = normalize_argv(arg, 3) 4624 if key is None: 4625 self._missing_argument() 4626 4627 result = peda.xormem(start, end, key) 4628 if result is not None: 4629 msg("XORed data (first 32 bytes):") 4630 msg('"' + to_hexstr(result[:32]) + '"') 4631 return 4632 4633 # searchmem(), searchmem_by_range() 4634 def searchmem(self, *arg): 4635 """ 4636 Search for a pattern in memory; support regex search 4637 Usage: 4638 MYNAME pattern start end 4639 MYNAME pattern mapname 4640 """ 4641 (pattern, start, end) = normalize_argv(arg, 3) 4642 (pattern, mapname) = normalize_argv(arg, 2) 4643 if pattern is None: 4644 self._missing_argument() 4645 4646 pattern = arg[0] 4647 result = [] 4648 if end is None and to_int(mapname): 4649 vmrange = peda.get_vmrange(mapname) 4650 if vmrange: 4651 (start, end, _, _) = vmrange 4652 4653 if end is None: 4654 msg("Searching for %s in: %s ranges" % (repr(pattern), mapname)) 4655 result = peda.searchmem_by_range(mapname, pattern) 4656 else: 4657 msg("Searching for %s in range: 0x%x - 0x%x" % (repr(pattern), start, end)) 4658 result = peda.searchmem(start, end, pattern) 4659 4660 text = peda.format_search_result(result) 4661 pager(text) 4662 4663 return 4664 4665 # search_reference() 4666 def refsearch(self, *arg): 4667 """ 4668 Search for all references to a value in memory ranges 4669 Usage: 4670 MYNAME value mapname 4671 MYNAME value (search in all memory ranges) 4672 """ 4673 (search, mapname) = normalize_argv(arg, 2) 4674 if search is None: 4675 self._missing_argument() 4676 4677 search = arg[0] 4678 if mapname is None: 4679 mapname = "all" 4680 msg("Searching for reference to: %s in: %s ranges" % (repr(search), mapname)) 4681 result = peda.search_reference(search, mapname) 4682 4683 text = peda.format_search_result(result) 4684 pager(text) 4685 4686 return 4687 4688 # search_address(), search_pointer() 4689 def lookup(self, *arg): 4690 """ 4691 Search for all addresses/references to addresses which belong to a memory range 4692 Usage: 4693 MYNAME address searchfor belongto 4694 MYNAME pointer searchfor belongto 4695 """ 4696 (option, searchfor, belongto) = normalize_argv(arg, 3) 4697 if option is None: 4698 self._missing_argument() 4699 4700 result = [] 4701 if searchfor is None: 4702 searchfor = "stack" 4703 if belongto is None: 4704 belongto = "binary" 4705 4706 if option == "pointer": 4707 msg("Searching for pointers on: %s pointed to: %s, this may take minutes to complete..." % (searchfor, belongto)) 4708 result = peda.search_pointer(searchfor, belongto) 4709 if option == "address": 4710 msg("Searching for addresses on: %s belong to: %s, this may take minutes to complete..." % (searchfor, belongto)) 4711 result = peda.search_address(searchfor, belongto) 4712 4713 text = peda.format_search_result(result, 0) 4714 pager(text) 4715 4716 return 4717 lookup.options = ["address", "pointer"] 4718 4719 # examine_mem_reference() 4720 def telescope(self, *arg): 4721 """ 4722 Display memory content at an address with smart dereferences 4723 Usage: 4724 MYNAME [linecount] (analyze at current $SP) 4725 MYNAME address [linecount] 4726 """ 4727 4728 (address, count) = normalize_argv(arg, 2) 4729 4730 if self._is_running(): 4731 sp = peda.getreg("sp") 4732 else: 4733 sp = None 4734 4735 if count is None: 4736 count = 8 4737 if address is None: 4738 address = sp 4739 elif address < 0x1000: 4740 count = address 4741 address = sp 4742 4743 if not address: 4744 return 4745 4746 step = peda.intsize() 4747 if not peda.is_address(address): # cannot determine address 4748 msg("Invalid $SP address: 0x%x" % address, "red") 4749 return 4750 for i in range(count): 4751 if not peda.execute("x/%sx 0x%x" % ("g" if step == 8 else "w", address + i*step)): 4752 break 4753 return 4754 4755 result = [] 4756 for i in range(count): 4757 value = address + i*step 4758 if peda.is_address(value): 4759 result += [peda.examine_mem_reference(value)] 4760 else: 4761 result += [None] 4762 idx = 0 4763 text = "" 4764 for chain in result: 4765 text += "%04d| " % (idx) 4766 text += format_reference_chain(chain) 4767 text += "\n" 4768 idx += step 4769 4770 pager(text) 4771 4772 return 4773 4774 def eflags(self, *arg): 4775 """ 4776 Display/set/clear/toggle value of eflags register 4777 Usage: 4778 MYNAME 4779 MYNAME [set|clear|toggle] flagname 4780 """ 4781 FLAGS = ["CF", "PF", "AF", "ZF", "SF", "TF", "IF", "DF", "OF"] 4782 FLAGS_TEXT = ["Carry", "Parity", "Adjust", "Zero", "Sign", "Trap", 4783 "Interrupt", "Direction", "Overflow"] 4784 4785 (option, flagname) = normalize_argv(arg, 2) 4786 if not self._is_running(): 4787 return 4788 4789 elif option and not flagname: 4790 self._missing_argument() 4791 4792 elif option is None: # display eflags 4793 flags = peda.get_eflags() 4794 text = "" 4795 for (i, f) in enumerate(FLAGS): 4796 if flags[f]: 4797 text += "%s " % red(FLAGS_TEXT[i].upper(), "bold") 4798 else: 4799 text += "%s " % green(FLAGS_TEXT[i].lower()) 4800 4801 eflags = peda.getreg("eflags") 4802 msg("%s: 0x%x (%s)" % (green("EFLAGS"), eflags, text.strip())) 4803 4804 elif option == "set": 4805 peda.set_eflags(flagname, True) 4806 4807 elif option == "clear": 4808 peda.set_eflags(flagname, False) 4809 4810 elif option == "toggle": 4811 peda.set_eflags(flagname, None) 4812 4813 return 4814 eflags.options = ["set", "clear", "toggle"] 4815 4816 def xinfo(self, *arg): 4817 """ 4818 Display detail information of address/registers 4819 Usage: 4820 MYNAME address 4821 MYNAME register [reg1 reg2] 4822 """ 4823 4824 (address, regname) = normalize_argv(arg, 2) 4825 if address is None: 4826 self._missing_argument() 4827 4828 text = "" 4829 if not self._is_running(): 4830 return 4831 4832 def get_reg_text(r, v): 4833 text = green("%s" % r.upper().ljust(3)) + ": " 4834 chain = peda.examine_mem_reference(v) 4835 text += format_reference_chain(chain) 4836 text += "\n" 4837 return text 4838 4839 (arch, bits) = peda.getarch() 4840 if str(address).startswith("r"): 4841 # Register 4842 regs = peda.getregs(" ".join(arg[1:])) 4843 if regname is None: 4844 for r in REGISTERS[bits]: 4845 if r in regs: 4846 text += get_reg_text(r, regs[r]) 4847 else: 4848 for (r, v) in sorted(regs.items()): 4849 text += get_reg_text(r, v) 4850 if text: 4851 msg(text.strip()) 4852 if regname is None or "eflags" in regname: 4853 self.eflags() 4854 return 4855 4856 elif to_int(address) is None: 4857 warning_msg("not a register nor an address") 4858 else: 4859 # Address 4860 chain = peda.examine_mem_reference(address, depth=0) 4861 text += format_reference_chain(chain) + "\n" 4862 vmrange = peda.get_vmrange(address) 4863 if vmrange: 4864 (start, end, perm, name) = vmrange 4865 text += "Virtual memory mapping:\n" 4866 text += green("Start : %s\n" % to_address(start)) 4867 text += green("End : %s\n" % to_address(end)) 4868 text += yellow("Offset: 0x%x\n" % (address-start)) 4869 text += red("Perm : %s\n" % perm) 4870 text += blue("Name : %s" % name) 4871 msg(text) 4872 4873 return 4874 xinfo.options = ["register"] 4875 4876 def strings(self, *arg): 4877 """ 4878 Display printable strings in memory 4879 Usage: 4880 MYNAME start end [minlen] 4881 MYNAME mapname [minlen] 4882 MYNAME (display all printable strings in binary - slow) 4883 """ 4884 (start, end, minlen) = normalize_argv(arg, 3) 4885 4886 mapname = None 4887 if start is None: 4888 mapname = "binary" 4889 elif to_int(start) is None or (end < start): 4890 (mapname, minlen) = normalize_argv(arg, 2) 4891 4892 if minlen is None: 4893 minlen = 1 4894 4895 if mapname: 4896 maps = peda.get_vmmap(mapname) 4897 else: 4898 maps = [(start, end, None, None)] 4899 4900 if not maps: 4901 warning_msg("failed to get memory map for %s" % mapname) 4902 return 4903 4904 text = "" 4905 regex_pattern = "[%s]{%d,}" % (re.escape(string.printable), minlen) 4906 p = re.compile(regex_pattern.encode('utf-8')) 4907 for (start, end, _, _) in maps: 4908 mem = peda.dumpmem(start, end) 4909 if not mem: continue 4910 found = p.finditer(mem) 4911 if not found: continue 4912 4913 for m in found: 4914 text += "0x%x: %s\n" % (start+m.start(), string_repr(mem[m.start():m.end()].strip(), show_quotes=False)) 4915 4916 pager(text) 4917 return 4918 4919 def sgrep(self, *arg): 4920 """ 4921 Search for full strings contain the given pattern 4922 Usage: 4923 MYNAME pattern start end 4924 MYNAME pattern mapname 4925 MYNAME pattern 4926 """ 4927 (pattern,) = normalize_argv(arg, 1) 4928 4929 if pattern is None: 4930 self._missing_argument() 4931 arg = list(arg[1:]) 4932 if not arg: 4933 arg = ["binary"] 4934 4935 pattern = "[^\x00]*%s[^\x00]*" % pattern 4936 self.searchmem(pattern, *arg) 4937 4938 return 4939 4940 4941 ############################### 4942 # Exploit Helper Commands # 4943 ############################### 4944 # elfheader() 4945 def elfheader(self, *arg): 4946 """ 4947 Get headers information from debugged ELF file 4948 Usage: 4949 MYNAME [header_name] 4950 """ 4951 4952 (name,) = normalize_argv(arg, 1) 4953 result = peda.elfheader(name) 4954 if len(result) == 0: 4955 warning_msg("%s not found, did you specify the FILE to debug?" % (name if name else "headers")) 4956 elif len(result) == 1: 4957 (k, (start, end, type)) = list(result.items())[0] 4958 msg("%s: 0x%x - 0x%x (%s)" % (k, start, end, type)) 4959 else: 4960 for (k, (start, end, type)) in sorted(result.items(), key=lambda x: x[1]): 4961 msg("%s = 0x%x" % (k, start)) 4962 return 4963 4964 # readelf_header(), elfheader_solib() 4965 def readelf(self, *arg): 4966 """ 4967 Get headers information from an ELF file 4968 Usage: 4969 MYNAME mapname [header_name] 4970 MYNAME filename [header_name] 4971 """ 4972 4973 (filename, hname) = normalize_argv(arg, 2) 4974 result = {} 4975 maps = peda.get_vmmap() 4976 if filename is None: # fallback to elfheader() 4977 result = peda.elfheader() 4978 else: 4979 result = peda.elfheader_solib(filename, hname) 4980 4981 if not result: 4982 result = peda.readelf_header(filename, hname) 4983 if len(result) == 0: 4984 warning_msg("%s or %s not found" % (filename, hname)) 4985 elif len(result) == 1: 4986 (k, (start, end, type)) = list(result.items())[0] 4987 msg("%s: 0x%x - 0x%x (%s)" % (k, start, end, type)) 4988 else: 4989 for (k, (start, end, type)) in sorted(result.items(), key=lambda x: x[1]): 4990 msg("%s = 0x%x" % (k, start)) 4991 return 4992 4993 # elfsymbol() 4994 def elfsymbol(self, *arg): 4995 """ 4996 Get non-debugging symbol information from an ELF file 4997 Usage: 4998 MYNAME symbol_name 4999 """ 5000 (name,) = normalize_argv(arg, 1) 5001 if not peda.getfile(): 5002 warning_msg("please specify a file to debug") 5003 return 5004 5005 result = peda.elfsymbol(name) 5006 if len(result) == 0: 5007 msg("'%s': no match found" % (name if name else "plt symbols")) 5008 else: 5009 if ("%s@got" % name) not in result: 5010 msg("Found %d symbols" % len(result)) 5011 else: 5012 msg("Detail symbol info") 5013 for (k, v) in sorted(result.items(), key=lambda x: x[1]): 5014 msg("%s = %s" % (k, "0x%x" % v if v else repr(v))) 5015 return 5016 5017 # checksec() 5018 def checksec(self, *arg): 5019 """ 5020 Check for various security options of binary 5021 For full features, use http://www.trapkit.de/tools/checksec.sh 5022 Usage: 5023 MYNAME [file] 5024 """ 5025 (filename,) = normalize_argv(arg, 1) 5026 colorcodes = { 5027 0: red("disabled"), 5028 1: green("ENABLED"), 5029 2: yellow("Partial"), 5030 3: green("FULL"), 5031 4: yellow("Dynamic Shared Object"), 5032 } 5033 5034 result = peda.checksec(filename) 5035 if result: 5036 for (k, v) in sorted(result.items()): 5037 msg("%s: %s" % (k.ljust(10), colorcodes[v])) 5038 return 5039 5040 def nxtest(self, *arg): 5041 """ 5042 Perform real NX test to see if it is enabled/supported by OS 5043 Usage: 5044 MYNAME [address] 5045 """ 5046 (address,) = normalize_argv(arg, 1) 5047 5048 exec_wrapper = peda.execute_redirect("show exec-wrapper").split('"')[1] 5049 if exec_wrapper != "": 5050 peda.execute("unset exec-wrapper") 5051 5052 if not peda.getpid(): # start program if not running 5053 peda.execute("start") 5054 5055 # set current PC => address, continue 5056 pc = peda.getreg("pc") 5057 sp = peda.getreg("sp") 5058 if not address: 5059 address = sp 5060 peda.execute("set $pc = 0x%x" % address) 5061 # set value at address => 0xcc 5062 peda.execute("set *0x%x = 0x%x" % (address, 0xcccccccc)) 5063 peda.execute("set *0x%x = 0x%x" % (address+4, 0xcccccccc)) 5064 out = peda.execute_redirect("continue") 5065 text = "NX test at %s: " % (to_address(address) if address != sp else "stack") 5066 5067 if out: 5068 if "SIGSEGV" in out: 5069 text += red("Non-Executable") 5070 elif "SIGTRAP" in out: 5071 text += green("Executable") 5072 else: 5073 text += "Failed to test" 5074 5075 msg(text) 5076 # restore exec-wrapper 5077 if exec_wrapper != "": 5078 peda.execute("set exec-wrapper %s" % exec_wrapper) 5079 5080 return 5081 5082 # search_asm() 5083 def asmsearch(self, *arg): 5084 """ 5085 Search for ASM instructions in memory 5086 Usage: 5087 MYNAME "asmcode" start end 5088 MYNAME "asmcode" mapname 5089 """ 5090 (asmcode, start, end) = normalize_argv(arg, 3) 5091 if asmcode is None: 5092 self._missing_argument() 5093 5094 if not self._is_running(): 5095 return 5096 5097 asmcode = arg[0] 5098 result = [] 5099 if end is None: 5100 mapname = start 5101 if mapname is None: 5102 mapname = "binary" 5103 maps = peda.get_vmmap(mapname) 5104 msg("Searching for ASM code: %s in: %s ranges" % (repr(asmcode), mapname)) 5105 for (start, end, _, _) in maps: 5106 if not peda.is_executable(start, maps): continue # skip non-executable page 5107 result += peda.search_asm(start, end, asmcode) 5108 else: 5109 msg("Searching for ASM code: %s in range: 0x%x - 0x%x" % (repr(asmcode), start, end)) 5110 result = peda.search_asm(start, end, asmcode) 5111 5112 text = "Not found" 5113 if result: 5114 text = "" 5115 for (addr, (byte, code)) in result: 5116 text += "%s : (%s)\t%s\n" % (to_address(addr), byte.decode('utf-8'), code) 5117 pager(text) 5118 5119 return 5120 5121 # search_asm() 5122 def ropsearch(self, *arg): 5123 """ 5124 Search for ROP gadgets in memory 5125 Note: only for simple gadgets, for full ROP search try: http://ropshell.com 5126 Usage: 5127 MYNAME "gadget" start end 5128 MYNAME "gadget" pagename 5129 """ 5130 5131 (asmcode, start, end) = normalize_argv(arg, 3) 5132 if asmcode is None: 5133 self._missing_argument() 5134 5135 if not self._is_running(): 5136 return 5137 5138 asmcode = arg[0] 5139 result = [] 5140 if end is None: 5141 if start is None: 5142 mapname = "binary" 5143 else: 5144 mapname = start 5145 maps = peda.get_vmmap(mapname) 5146 msg("Searching for ROP gadget: %s in: %s ranges" % (repr(asmcode), mapname)) 5147 for (start, end, _, _) in maps: 5148 if not peda.is_executable(start, maps): continue # skip non-executable page 5149 result += peda.search_asm(start, end, asmcode, rop=1) 5150 else: 5151 msg("Searching for ROP gadget: %s in range: 0x%x - 0x%x" % (repr(asmcode), start, end)) 5152 result = peda.search_asm(start, end, asmcode, rop=1) 5153 5154 result = sorted(result, key=lambda x: len(x[1][0])) 5155 text = "Not found" 5156 if result: 5157 text = "" 5158 for (addr, (byte, code)) in result: 5159 text += "%s : (%s)\t%s\n" % (to_address(addr), byte, code) 5160 pager(text) 5161 5162 return 5163 5164 # dumprop() 5165 def dumprop(self, *arg): 5166 """ 5167 Dump all ROP gadgets in specific memory range 5168 Note: only for simple gadgets, for full ROP search try: http://ropshell.com 5169 Warning: this can be very slow, do not run for big memory range 5170 Usage: 5171 MYNAME start end [keyword] [depth] 5172 MYNAME mapname [keyword] 5173 default gadget instruction depth is: 5 5174 """ 5175 5176 (start, end, keyword, depth) = normalize_argv(arg, 4) 5177 filename = peda.getfile() 5178 if filename is None: 5179 warning_msg("please specify a filename to debug") 5180 return 5181 5182 filename = os.path.basename(filename) 5183 mapname = None 5184 if start is None: 5185 mapname = "binary" 5186 elif end is None: 5187 mapname = start 5188 elif to_int(end) is None: 5189 mapname = start 5190 keyword = end 5191 5192 if depth is None: 5193 depth = 5 5194 5195 result = {} 5196 warning_msg("this can be very slow, do not run for large memory range") 5197 if mapname: 5198 maps = peda.get_vmmap(mapname) 5199 for (start, end, _, _) in maps: 5200 if not peda.is_executable(start, maps): continue # skip non-executable page 5201 result.update(peda.dumprop(start, end, keyword)) 5202 else: 5203 result.update(peda.dumprop(start, end, keyword)) 5204 5205 text = "Not found" 5206 if len(result) > 0: 5207 text = "" 5208 outfile = "%s-rop.txt" % filename 5209 fd = open(outfile, "w") 5210 msg("Writing ROP gadgets to file: %s ..." % outfile) 5211 for (code, addr) in sorted(result.items(), key = lambda x:len(x[0])): 5212 text += "0x%x: %s\n" % (addr, code) 5213 fd.write("0x%x: %s\n" % (addr, code)) 5214 fd.close() 5215 5216 pager(text) 5217 return 5218 5219 # common_rop_gadget() 5220 def ropgadget(self, *arg): 5221 """ 5222 Get common ROP gadgets of binary or library 5223 Usage: 5224 MYNAME [mapname] 5225 """ 5226 5227 (mapname,) = normalize_argv(arg, 1) 5228 result = peda.common_rop_gadget(mapname) 5229 if not result: 5230 msg("Not found") 5231 else: 5232 text = "" 5233 for (k, v) in sorted(result.items(), key=lambda x: len(x[0]) if not x[0].startswith("add") else int(x[0].split("_")[1])): 5234 text += "%s = 0x%x\n" % (k, v) 5235 pager(text) 5236 5237 return 5238 5239 # search_jmpcall() 5240 def jmpcall(self, *arg): 5241 """ 5242 Search for JMP/CALL instructions in memory 5243 Usage: 5244 MYNAME (search all JMP/CALL in current binary) 5245 MYNAME reg [mapname] 5246 MYNAME reg start end 5247 """ 5248 5249 (reg, start, end) = normalize_argv(arg, 3) 5250 result = [] 5251 if not self._is_running(): 5252 return 5253 5254 mapname = None 5255 if start is None: 5256 mapname = "binary" 5257 elif end is None: 5258 mapname = start 5259 5260 if mapname: 5261 maps = peda.get_vmmap(mapname) 5262 for (start, end, _, _) in maps: 5263 if not peda.is_executable(start, maps): continue 5264 result += peda.search_jmpcall(start, end, reg) 5265 else: 5266 result = peda.search_jmpcall(start, end, reg) 5267 5268 if not result: 5269 msg("Not found") 5270 else: 5271 text = "" 5272 for (a, v) in result: 5273 text += "0x%x : %s\n" % (a, v) 5274 pager(text) 5275 5276 return 5277 5278 # cyclic_pattern() 5279 def pattern_create(self, *arg): 5280 """ 5281 Generate a cyclic pattern 5282 Set "pattern" option for basic/extended pattern type 5283 Usage: 5284 MYNAME size [file] 5285 """ 5286 5287 (size, filename) = normalize_argv(arg, 2) 5288 if size is None: 5289 self._missing_argument() 5290 5291 pattern = cyclic_pattern(size) 5292 if filename is not None: 5293 open(filename, "wb").write(pattern) 5294 msg("Writing pattern of %d chars to filename \"%s\"" % (len(pattern), filename)) 5295 else: 5296 msg("'" + pattern.decode('utf-8') + "'") 5297 5298 return 5299 5300 # cyclic_pattern() 5301 def pattern_offset(self, *arg): 5302 """ 5303 Search for offset of a value in cyclic pattern 5304 Set "pattern" option for basic/extended pattern type 5305 Usage: 5306 MYNAME value 5307 """ 5308 5309 (value,) = normalize_argv(arg, 1) 5310 if value is None: 5311 self._missing_argument() 5312 5313 pos = cyclic_pattern_offset(value) 5314 if pos is None: 5315 msg("%s not found in pattern buffer" % value) 5316 else: 5317 msg("%s found at offset: %d" % (value, pos)) 5318 5319 return 5320 5321 # cyclic_pattern(), searchmem_*() 5322 def pattern_search(self, *arg): 5323 """ 5324 Search a cyclic pattern in registers and memory 5325 Set "pattern" option for basic/extended pattern type 5326 Usage: 5327 MYNAME 5328 """ 5329 def nearby_offset(v): 5330 for offset in range(-128, 128, 4): 5331 pos = cyclic_pattern_offset(v + offset) 5332 if pos is not None: 5333 return (pos, offset) 5334 return None 5335 5336 if not self._is_running(): 5337 return 5338 5339 reg_result = {} 5340 regs = peda.getregs() 5341 5342 # search for registers with value in pattern buffer 5343 for (r, v) in regs.items(): 5344 if len(to_hex(v)) < 8: continue 5345 res = nearby_offset(v) 5346 if res: 5347 reg_result[r] = res 5348 5349 if reg_result: 5350 msg("Registers contain pattern buffer:", "red") 5351 for (r, (p, o)) in reg_result.items(): 5352 msg("%s+%d found at offset: %d" % (r.upper(), o, p)) 5353 else: 5354 msg("No register contains pattern buffer") 5355 5356 # search for registers which point to pattern buffer 5357 reg_result = {} 5358 for (r, v) in regs.items(): 5359 if not peda.is_address(v): continue 5360 chain = peda.examine_mem_reference(v) 5361 (v, t, vn) = chain[-1] 5362 if not vn: continue 5363 o = cyclic_pattern_offset(vn.strip("'").strip('"')[:4]) 5364 if o is not None: 5365 reg_result[r] = (len(chain), len(vn)-2, o) 5366 5367 if reg_result: 5368 msg("Registers point to pattern buffer:", "yellow") 5369 for (r, (d, l, o)) in reg_result.items(): 5370 msg("[%s] %s offset %d - size ~%d" % (r.upper(), "-->"*d, o, l)) 5371 else: 5372 msg("No register points to pattern buffer") 5373 5374 # search for pattern buffer in memory 5375 maps = peda.get_vmmap() 5376 search_result = [] 5377 for (start, end, perm, name) in maps: 5378 if "w" not in perm: continue # only search in writable memory 5379 res = cyclic_pattern_search(peda.dumpmem(start, end)) 5380 for (a, l, o) in res: 5381 a += start 5382 search_result += [(a, l, o)] 5383 5384 sp = peda.getreg("sp") 5385 if search_result: 5386 msg("Pattern buffer found at:", "green") 5387 for (a, l, o) in search_result: 5388 ranges = peda.get_vmrange(a) 5389 text = "%s : offset %4d - size %4d" % (to_address(a), o, l) 5390 if ranges[3] == "[stack]": 5391 text += " ($sp + %s [%d dwords])" % (to_hex(a-sp), (a-sp)//4) 5392 else: 5393 text += " (%s)" % ranges[3] 5394 msg(text) 5395 else: 5396 msg("Pattern buffer not found in memory") 5397 5398 # search for references to pattern buffer in memory 5399 ref_result = [] 5400 for (a, l, o) in search_result: 5401 res = peda.searchmem_by_range("all", "0x%x" % a) 5402 ref_result += [(x[0], a) for x in res] 5403 if len(ref_result) > 0: 5404 msg("References to pattern buffer found at:", "blue") 5405 for (a, v) in ref_result: 5406 ranges = peda.get_vmrange(a) 5407 text = "%s : %s" % (to_address(a), to_address(v)) 5408 if ranges[3] == "[stack]": 5409 text += " ($sp + %s [%d dwords])" % (to_hex(a-sp), (a-sp)//4) 5410 else: 5411 text += " (%s)" % ranges[3] 5412 msg(text) 5413 else: 5414 msg("Reference to pattern buffer not found in memory") 5415 5416 return 5417 5418 # cyclic_pattern(), writemem() 5419 def pattern_patch(self, *arg): 5420 """ 5421 Write a cyclic pattern to memory 5422 Set "pattern" option for basic/extended pattern type 5423 Usage: 5424 MYNAME address size 5425 """ 5426 5427 (address, size) = normalize_argv(arg, 2) 5428 if size is None: 5429 self._missing_argument() 5430 5431 pattern = cyclic_pattern(size) 5432 num_bytes_written = peda.writemem(address, pattern) 5433 if num_bytes_written: 5434 msg("Written %d chars of cyclic pattern to 0x%x" % (size, address)) 5435 else: 5436 msg("Failed to write to memory") 5437 5438 return 5439 5440 # cyclic_pattern() 5441 def pattern_arg(self, *arg): 5442 """ 5443 Set argument list with cyclic pattern 5444 Set "pattern" option for basic/extended pattern type 5445 Usage: 5446 MYNAME size1 [size2,offset2] ... 5447 """ 5448 5449 if not arg: 5450 self._missing_argument() 5451 5452 arglist = [] 5453 for a in arg: 5454 (size, offset) = (a + ",").split(",")[:2] 5455 if offset: 5456 offset = to_int(offset) 5457 else: 5458 offset = 0 5459 size = to_int(size) 5460 if size is None or offset is None: 5461 self._missing_argument() 5462 5463 # try to generate unique, non-overlapped patterns 5464 if arglist and offset == 0: 5465 offset = sum(arglist[-1]) 5466 arglist += [(size, offset)] 5467 5468 patterns = [] 5469 for (s, o) in arglist: 5470 patterns += ["\'%s\'" % cyclic_pattern(s, o).decode('utf-8')] 5471 peda.execute("set arg %s" % " ".join(patterns)) 5472 msg("Set %d arguments to program" % len(patterns)) 5473 5474 return 5475 5476 # cyclic_pattern() 5477 def pattern_env(self, *arg): 5478 """ 5479 Set environment variable with a cyclic pattern 5480 Set "pattern" option for basic/extended pattern type 5481 Usage: 5482 MYNAME ENVNAME size[,offset] 5483 """ 5484 5485 (env, size) = normalize_argv(arg, 2) 5486 if size is None: 5487 self._missing_argument() 5488 5489 (size, offset) = (arg[1] + ",").split(",")[:2] 5490 size = to_int(size) 5491 if offset: 5492 offset = to_int(offset) 5493 else: 5494 offset = 0 5495 if size is None or offset is None: 5496 self._missing_argument() 5497 5498 peda.execute("set env %s %s" % (env, cyclic_pattern(size, offset).decode('utf-8'))) 5499 msg("Set environment %s = cyclic_pattern(%d, %d)" % (env, size, offset)) 5500 5501 return 5502 5503 def pattern(self, *arg): 5504 """ 5505 Generate, search, or write a cyclic pattern to memory 5506 Set "pattern" option for basic/extended pattern type 5507 Usage: 5508 MYNAME create size [file] 5509 MYNAME offset value 5510 MYNAME search 5511 MYNAME patch address size 5512 MYNAME arg size1 [size2,offset2] 5513 MYNAME env size[,offset] 5514 """ 5515 5516 options = ["create", "offset", "search", "patch", "arg", "env"] 5517 (opt,) = normalize_argv(arg, 1) 5518 if opt is None or opt not in options: 5519 self._missing_argument() 5520 5521 func = getattr(self, "pattern_%s" % opt) 5522 func(*arg[1:]) 5523 5524 return 5525 pattern.options = ["create", "offset", "search", "patch", "arg", "env"] 5526 5527 def substr(self, *arg): 5528 """ 5529 Search for substrings of a given string/number in memory 5530 Commonly used for ret2strcpy ROP exploit 5531 Usage: 5532 MYNAME "string" start end 5533 MYNAME "string" [mapname] (default is search in current binary) 5534 """ 5535 (search, start, end) = normalize_argv(arg, 3) 5536 if search is None: 5537 self._missing_argument() 5538 5539 result = [] 5540 search = arg[0] 5541 mapname = None 5542 if start is None: 5543 mapname = "binary" 5544 elif end is None: 5545 mapname = start 5546 5547 if mapname: 5548 msg("Searching for sub strings of: %s in: %s ranges" % (repr(search), mapname)) 5549 maps = peda.get_vmmap(mapname) 5550 for (start, end, perm, _) in maps: 5551 if perm == "---p": # skip private range 5552 continue 5553 result = peda.search_substr(start, end, search) 5554 if result: # return the first found result 5555 break 5556 else: 5557 msg("Searching for sub strings of: %s in range: 0x%x - 0x%x" % (repr(search), start, end)) 5558 result = peda.search_substr(start, end, search) 5559 5560 if result: 5561 msg("# (address, target_offset), # value (address=0xffffffff means not found)") 5562 offset = 0 5563 for (k, v) in result: 5564 msg("(0x%x, %d), # %s" % ((0xffffffff if v == -1 else v), offset, string_repr(k))) 5565 offset += len(k) 5566 else: 5567 msg("Not found") 5568 5569 return 5570 5571 def assemble(self, *arg): 5572 """ 5573 On the fly assemble and execute instructions using NASM 5574 Usage: 5575 MYNAME [mode] [address] 5576 mode: -b16 / -b32 / -b64 5577 """ 5578 (mode, address) = normalize_argv(arg, 2) 5579 5580 exec_mode = 0 5581 write_mode = 0 5582 if to_int(mode) is not None: 5583 address, mode = mode, None 5584 5585 (arch, bits) = peda.getarch() 5586 if mode is None: 5587 mode = bits 5588 else: 5589 mode = to_int(mode[2:]) 5590 if mode not in [16, 32, 64]: 5591 self._missing_argument() 5592 5593 if self._is_running() and address == peda.getreg("pc"): 5594 write_mode = exec_mode = 1 5595 5596 line = peda.execute_redirect("show write") 5597 if line and "on" in line.split()[-1]: 5598 write_mode = 1 5599 5600 if address is None or mode != bits: 5601 write_mode = exec_mode = 0 5602 5603 if write_mode: 5604 msg("Instruction will be written to 0x%x" % address) 5605 else: 5606 msg("Instructions will be written to stdout") 5607 5608 msg("Type instructions (NASM syntax), one or more per line separated by \";\"") 5609 msg("End with a line saying just \"end\"") 5610 5611 if not write_mode: 5612 address = 0xdeadbeef 5613 5614 inst_list = [] 5615 inst_code = b"" 5616 # fetch instruction loop 5617 while True: 5618 inst = input("iasm|0x%x> " % address) 5619 if inst == "end": 5620 break 5621 if inst == "": 5622 continue 5623 bincode = peda.assemble(inst, mode) 5624 size = len(bincode) 5625 if size == 0: 5626 continue 5627 inst_list.append((size, bincode, inst)) 5628 if write_mode: 5629 peda.writemem(address, bincode) 5630 # execute assembled code 5631 if exec_mode: 5632 peda.execute("stepi %d" % (inst.count(";")+1)) 5633 5634 address += size 5635 inst_code += bincode 5636 msg("hexify: \"%s\"" % to_hexstr(bincode)) 5637 5638 text = Nasm.format_shellcode(b"".join([x[1] for x in inst_list]), mode) 5639 if text: 5640 msg("Assembled%s instructions:" % ("/Executed" if exec_mode else "")) 5641 msg(text) 5642 msg("hexify: \"%s\"" % to_hexstr(inst_code)) 5643 5644 return 5645 5646 5647 #################################### 5648 # Payload/Shellcode Generation # 5649 #################################### 5650 def skeleton(self, *arg): 5651 """ 5652 Generate python exploit code template 5653 Usage: 5654 MYNAME type [file] 5655 type = argv: local exploit via argument 5656 type = env: local exploit via crafted environment (including NULL byte) 5657 type = stdin: local exploit via stdin 5658 type = remote: remote exploit via TCP socket 5659 """ 5660 options = ["argv", "stdin", "env", "remote"] 5661 (opt, outfile) = normalize_argv(arg, 2) 5662 if opt not in options: 5663 self._missing_argument() 5664 5665 pattern = cyclic_pattern(20000).decode('utf-8') 5666 if opt == "argv": 5667 code = ExploitSkeleton().skeleton_local_argv 5668 if opt == "env": 5669 code = ExploitSkeleton().skeleton_local_env 5670 if opt == "stdin": 5671 code = ExploitSkeleton().skeleton_local_stdin 5672 if opt == "remote": 5673 code = ExploitSkeleton().skeleton_remote_tcp 5674 5675 if outfile: 5676 msg("Writing skeleton code to file \"%s\"" % outfile) 5677 open(outfile, "w").write(code.strip("\n")) 5678 os.chmod(outfile, 0o755) 5679 open("pattern.txt", "w").write(pattern) 5680 else: 5681 msg(code) 5682 5683 return 5684 skeleton.options = ["argv", "stdin", "env", "remote"] 5685 5686 def shellcode(self, *arg): 5687 """ 5688 Generate or download common shellcodes. 5689 Usage: 5690 MYNAME generate [arch/]platform type [port] [host] 5691 MYNAME search keyword (use % for any character wildcard) 5692 MYNAME display shellcodeId (shellcodeId as appears in search results) 5693 MYNAME zsc [generate customize shellcode] 5694 5695 For generate option: 5696 default port for bindport shellcode: 16706 (0x4142) 5697 default host/port for connect back shellcode: 127.127.127.127/16706 5698 supported arch: x86 5699 """ 5700 def list_shellcode(): 5701 """ 5702 List available shellcodes 5703 """ 5704 text = "Available shellcodes:\n" 5705 for arch in SHELLCODES: 5706 for platform in SHELLCODES[arch]: 5707 for sctype in SHELLCODES[arch][platform]: 5708 text += " %s/%s %s\n" % (arch, platform, sctype) 5709 msg(text) 5710 5711 """ Multiple variable name for different modes """ 5712 (mode, platform, sctype, port, host) = normalize_argv(arg, 5) 5713 (mode, keyword) = normalize_argv(arg, 2) 5714 (mode, shellcodeId) = normalize_argv(arg, 2) 5715 5716 if mode == "generate": 5717 arch = "x86" 5718 if platform and "/" in platform: 5719 (arch, platform) = platform.split("/") 5720 5721 if platform not in SHELLCODES[arch] or not sctype: 5722 list_shellcode() 5723 return 5724 #dbg_print_vars(arch, platform, sctype, port, host) 5725 try: 5726 sc = Shellcode(arch, platform).shellcode(sctype, port, host) 5727 except Exception as e: 5728 self._missing_argument() 5729 5730 if not sc: 5731 msg("Unknown shellcode") 5732 return 5733 5734 hexstr = to_hexstr(sc) 5735 linelen = 16 # display 16-bytes per line 5736 i = 0 5737 text = "# %s/%s/%s: %d bytes\n" % (arch, platform, sctype, len(sc)) 5738 if sctype in ["bindport", "connect"]: 5739 text += "# port=%s, host=%s\n" % (port if port else '16706', host if host else '127.127.127.127') 5740 text += "shellcode = (\n" 5741 while hexstr: 5742 text += ' "%s"\n' % (hexstr[:linelen*4]) 5743 hexstr = hexstr[linelen*4:] 5744 i += 1 5745 text += ")" 5746 msg(text) 5747 5748 # search shellcodes on shell-storm.org 5749 elif mode == "search": 5750 if keyword is None: 5751 self._missing_argument() 5752 5753 res_dl = Shellcode().search(keyword) 5754 if not res_dl: 5755 msg("Shellcode not found or cannot retrieve the result") 5756 return 5757 5758 msg("Found %d shellcodes" % len(res_dl)) 5759 msg("%s\t%s" %(blue("ScId"), blue("Title"))) 5760 text = "" 5761 for data_d in res_dl: 5762 text += "[%s]\t%s - %s\n" %(yellow(data_d['ScId']), data_d['ScArch'], data_d['ScTitle']) 5763 pager(text) 5764 5765 # download shellcodes from shell-storm.org 5766 elif mode == "display": 5767 if to_int(shellcodeId) is None: 5768 self._missing_argument() 5769 5770 res = Shellcode().display(shellcodeId) 5771 if not res: 5772 msg("Shellcode id not found or cannot retrieve the result") 5773 return 5774 5775 msg(res) 5776 #OWASP ZSC API Z3r0D4y.Com 5777 elif mode == "zsc": 5778 'os lists' 5779 oslist = ['linux_x86','linux_x64','linux_arm','linux_mips','freebsd_x86', 5780 'freebsd_x64','windows_x86','windows_x64','osx','solaris_x64','solaris_x86'] 5781 'functions' 5782 joblist = ['exec(\'/path/file\')','chmod(\'/path/file\',\'permission number\')','write(\'/path/file\',\'text to write\')', 5783 'file_create(\'/path/file\',\'text to write\')','dir_create(\'/path/folder\')','download(\'url\',\'filename\')', 5784 'download_execute(\'url\',\'filename\',\'command to execute\')','system(\'command to execute\')'] 5785 'encode types' 5786 encodelist = ['none','xor_random','xor_yourvalue','add_random','add_yourvalue','sub_random', 5787 'sub_yourvalue','inc','inc_timeyouwant','dec','dec_timeyouwant','mix_all'] 5788 try: 5789 while True: 5790 for os in oslist: 5791 msg('%s %s'%(yellow('[+]'),green(os))) 5792 if pyversion == 2: 5793 os = input('%s'%blue('os:')) 5794 if pyversion == 3: 5795 os = input('%s'%blue('os:')) 5796 if os in oslist: #check if os exist 5797 break 5798 else: 5799 warning_msg("Wrong input! Try Again.") 5800 while True: 5801 for job in joblist: 5802 msg('%s %s'%(yellow('[+]'),green(job))) 5803 if pyversion == 2: 5804 job = raw_input('%s'%blue('job:')) 5805 if pyversion == 3: 5806 job = input('%s'%blue('job:')) 5807 if job != '': 5808 break 5809 else: 5810 warning_msg("Please enter a function.") 5811 while True: 5812 for encode in encodelist: 5813 msg('%s %s'%(yellow('[+]'),green(encode))) 5814 if pyversion == 2: 5815 encode = raw_input('%s'%blue('encode:')) 5816 if pyversion == 3: 5817 encode = input('%s'%blue('encode:')) 5818 if encode != '': 5819 break 5820 else: 5821 warning_msg("Please enter a encode type.") 5822 except (KeyboardInterrupt, SystemExit): 5823 warning_msg("Aborted by user") 5824 result = Shellcode().zsc(os,job,encode) 5825 if result is not None: 5826 msg(result) 5827 else: 5828 pass 5829 return 5830 else: 5831 self._missing_argument() 5832 5833 return 5834 shellcode.options = ["generate", "search", "display","zsc"] 5835 5836 def gennop(self, *arg): 5837 """ 5838 Generate abitrary length NOP sled using given characters 5839 Usage: 5840 MYNAME size [chars] 5841 """ 5842 (size, chars) = normalize_argv(arg, 2) 5843 if size is None: 5844 self._missing_argument() 5845 5846 nops = Shellcode.gennop(size, chars) 5847 msg(repr(nops)) 5848 5849 return 5850 5851 def payload(self, *arg): 5852 """ 5853 Generate various type of ROP payload using ret2plt 5854 Usage: 5855 MYNAME copybytes (generate function template for ret2strcpy style payload) 5856 MYNAME copybytes dest1 data1 dest2 data2 ... 5857 """ 5858 (option,) = normalize_argv(arg, 1) 5859 if option is None: 5860 self._missing_argument() 5861 5862 if option == "copybytes": 5863 result = peda.payload_copybytes(template=1) # function template 5864 arg = arg[1:] 5865 while len(arg) > 0: 5866 (target, data) = normalize_argv(arg, 2) 5867 if data is None: 5868 break 5869 if to_int(data) is None: 5870 if data[0] == "[" and data[-1] == "]": 5871 data = eval(data) 5872 data = list2hexstr(data, peda.intsize()) 5873 else: 5874 data = "0x%x" % data 5875 result += peda.payload_copybytes(target, data) 5876 arg = arg[2:] 5877 5878 if not result: 5879 msg("Failed to construct payload") 5880 else: 5881 text = "" 5882 indent = to_int(config.Option.get("indent")) 5883 for line in result.splitlines(): 5884 text += " "*indent + line + "\n" 5885 msg(text) 5886 filename = peda.get_config_filename("payload") 5887 open(filename, "w").write(text) 5888 5889 return 5890 payload.options = ["copybytes"] 5891 5892 def snapshot(self, *arg): 5893 """ 5894 Save/restore process's snapshot to/from file 5895 Usage: 5896 MYNAME save file 5897 MYNAME restore file 5898 Warning: this is not thread safe, do not use with multithread program 5899 """ 5900 options = ["save", "restore"] 5901 (opt, filename) = normalize_argv(arg, 2) 5902 if opt not in options: 5903 self._missing_argument() 5904 5905 if not filename: 5906 filename = peda.get_config_filename("snapshot") 5907 5908 if opt == "save": 5909 if peda.save_snapshot(filename): 5910 msg("Saved process's snapshot to filename '%s'" % filename) 5911 else: 5912 msg("Failed to save process's snapshot") 5913 5914 if opt == "restore": 5915 if peda.restore_snapshot(filename): 5916 msg("Restored process's snapshot from filename '%s'" % filename) 5917 peda.execute("stop") 5918 else: 5919 msg("Failed to restore process's snapshot") 5920 5921 return 5922 snapshot.options = ["save", "restore"] 5923 5924 def crashdump(self, *arg): 5925 """ 5926 Display crashdump info and save to file 5927 Usage: 5928 MYNAME [reason_text] 5929 """ 5930 (reason,) = normalize_argv(arg, 1) 5931 if not reason: 5932 reason = "Interactive dump" 5933 5934 logname = peda.get_config_filename("crashlog") 5935 logfd = open(logname, "a") 5936 config.Option.set("_teefd", logfd) 5937 msg("[%s]" % "START OF CRASH DUMP".center(78, "-")) 5938 msg("Timestamp: %s" % time.ctime()) 5939 msg("Reason: %s" % red(reason)) 5940 5941 # exploitability 5942 pc = peda.getreg("pc") 5943 if not peda.is_address(pc): 5944 exp = red("EXPLOITABLE") 5945 else: 5946 exp = "Unknown" 5947 msg("Exploitability: %s" % exp) 5948 5949 # registers, code, stack 5950 self.context_register() 5951 self.context_code(16) 5952 self.context_stack() 5953 5954 # backtrace 5955 msg("[%s]" % "backtrace (innermost 10 frames)".center(78, "-"), "blue") 5956 msg(peda.execute_redirect("backtrace 10")) 5957 5958 msg("[%s]\n" % "END OF CRASH DUMP".center(78, "-")) 5959 config.Option.set("_teefd", "") 5960 logfd.close() 5961 5962 return 5963 5964 def utils(self, *arg): 5965 """ 5966 Miscelaneous utilities from utils module 5967 Usage: 5968 MYNAME command arg 5969 """ 5970 (command, carg) = normalize_argv(arg, 2) 5971 cmds = ["int2hexstr", "list2hexstr", "str2intlist"] 5972 if not command or command not in cmds or not carg: 5973 self._missing_argument() 5974 5975 func = globals()[command] 5976 if command == "int2hexstr": 5977 if to_int(carg) is None: 5978 msg("Not a number") 5979 return 5980 result = func(to_int(carg)) 5981 result = to_hexstr(result) 5982 5983 if command == "list2hexstr": 5984 if to_int(carg) is not None: 5985 msg("Not a list") 5986 return 5987 result = func(eval("%s" % carg)) 5988 result = to_hexstr(result) 5989 5990 if command == "str2intlist": 5991 res = func(carg) 5992 result = "[" 5993 for v in res: 5994 result += "%s, " % to_hex(v) 5995 result = result.rstrip(", ") + "]" 5996 5997 msg(result) 5998 return 5999 utils.options = ["int2hexstr", "list2hexstr", "str2intlist"] 6000 6001########################################################################### 6002class pedaGDBCommand(gdb.Command): 6003 """ 6004 Wrapper of gdb.Command for master "peda" command 6005 """ 6006 def __init__(self, cmdname="peda"): 6007 self.cmdname = cmdname 6008 self.__doc__ = pedacmd._get_helptext() 6009 super(pedaGDBCommand, self).__init__(self.cmdname, gdb.COMMAND_DATA) 6010 6011 def invoke(self, arg_string, from_tty): 6012 # do not repeat command 6013 self.dont_repeat() 6014 arg = peda.string_to_argv(arg_string) 6015 if len(arg) < 1: 6016 pedacmd.help() 6017 else: 6018 cmd = arg[0] 6019 if cmd in pedacmd.commands: 6020 func = getattr(pedacmd, cmd) 6021 try: 6022 # reset memoized cache 6023 reset_cache(sys.modules['__main__']) 6024 func(*arg[1:]) 6025 except Exception as e: 6026 if config.Option.get("debug") == "on": 6027 msg("Exception: %s" %e) 6028 traceback.print_exc() 6029 peda.restore_user_command("all") 6030 pedacmd.help(cmd) 6031 else: 6032 msg("Undefined command: %s. Try \"peda help\"" % cmd) 6033 return 6034 6035 def complete(self, text, word): 6036 completion = [] 6037 if text != "": 6038 cmd = text.split()[0] 6039 if cmd in pedacmd.commands: 6040 func = getattr(pedacmd, cmd) 6041 for opt in func.options: 6042 if word in opt: 6043 completion += [opt] 6044 else: 6045 for cmd in pedacmd.commands: 6046 if cmd.startswith(text.strip()): 6047 completion += [cmd] 6048 else: 6049 for cmd in pedacmd.commands: 6050 if word in cmd and cmd not in completion: 6051 completion += [cmd] 6052 return completion 6053 6054 6055########################################################################### 6056class Alias(gdb.Command): 6057 """ 6058 Generic alias, create short command names 6059 This doc should be changed dynamically 6060 """ 6061 def __init__(self, alias, command, shorttext=1): 6062 (cmd, opt) = (command + " ").split(" ", 1) 6063 if cmd == "peda" or cmd == "pead": 6064 cmd = opt.split(" ")[0] 6065 if not shorttext: 6066 self.__doc__ = pedacmd._get_helptext(cmd) 6067 else: 6068 self.__doc__ = green("Alias for '%s'" % command) 6069 self._command = command 6070 self._alias = alias 6071 super(Alias, self).__init__(alias, gdb.COMMAND_NONE) 6072 6073 def invoke(self, args, from_tty): 6074 self.dont_repeat() 6075 gdb.execute("%s %s" %(self._command, args)) 6076 6077 def complete(self, text, word): 6078 completion = [] 6079 cmd = self._command.split("peda ")[1] 6080 for opt in getattr(pedacmd, cmd).options: # list of command's options 6081 if text in opt and opt not in completion: 6082 completion += [opt] 6083 if completion != []: 6084 return completion 6085 if cmd in ["set", "show"] and text.split()[0] in ["option"]: 6086 opname = [x for x in config.OPTIONS.keys() if x.startswith(word.strip())] 6087 if opname != []: 6088 completion = opname 6089 else: 6090 completion = list(config.OPTIONS.keys()) 6091 return completion 6092 6093 6094########################################################################### 6095## INITIALIZATION ## 6096# global instances of PEDA() and PEDACmd() 6097peda = PEDA() 6098pedacmd = PEDACmd() 6099pedacmd.help.__func__.options = pedacmd.commands # XXX HACK 6100 6101# register "peda" command in gdb 6102pedaGDBCommand() 6103Alias("pead", "peda") # just for auto correction 6104 6105# create aliases for subcommands 6106for cmd in pedacmd.commands: 6107 func = getattr(pedacmd, cmd) 6108 func.__func__.__doc__ = func.__doc__.replace("MYNAME", cmd) 6109 if cmd not in ["help", "show", "set"]: 6110 Alias(cmd, "peda %s" % cmd, 0) 6111 6112# handle SIGINT / Ctrl-C 6113def sigint_handler(signal, frame): 6114 warning_msg("Got Ctrl+C / SIGINT!") 6115 gdb.execute("set logging off") 6116 peda.restore_user_command("all") 6117 raise KeyboardInterrupt 6118signal.signal(signal.SIGINT, sigint_handler) 6119 6120# custom hooks 6121peda.define_user_command("hook-stop", 6122 "peda context\n" 6123 "session autosave" 6124 ) 6125 6126# common used shell commands aliases 6127shellcmds = ["man", "ls", "ps", "grep", "cat", "more", "less", "pkill", "clear", "vi", "nano"] 6128for cmd in shellcmds: 6129 Alias(cmd, "shell %s" % cmd) 6130 6131# custom command aliases, add any alias you want 6132Alias("phelp", "peda help") 6133Alias("pset", "peda set") 6134Alias("pshow", "peda show") 6135Alias("pbreak", "peda pltbreak") 6136Alias("pattc", "peda pattern_create") 6137Alias("patto", "peda pattern_offset") 6138Alias("patta", "peda pattern_arg") 6139Alias("patte", "peda pattern_env") 6140Alias("patts", "peda pattern_search") 6141Alias("find", "peda searchmem") # override gdb find command 6142Alias("ftrace", "peda tracecall") 6143Alias("itrace", "peda traceinst") 6144Alias("jtrace", "peda traceinst j") 6145Alias("stack", "peda telescope $sp") 6146Alias("viewmem", "peda telescope") 6147Alias("reg", "peda xinfo register") 6148Alias("brva", "breakrva") 6149 6150# misc gdb settings 6151peda.execute("set confirm off") 6152peda.execute("set verbose off") 6153peda.execute("set output-radix 0x10") 6154peda.execute("set prompt \001%s\002" % red("\002gdb-peda$ \001")) # custom prompt 6155peda.execute("set height 0") # disable paging 6156peda.execute("set history expansion on") 6157peda.execute("set history save on") # enable history saving 6158peda.execute("set disassembly-flavor intel") 6159peda.execute("set follow-fork-mode child") 6160peda.execute("set backtrace past-main on") 6161peda.execute("set step-mode on") 6162peda.execute("set print pretty on") 6163peda.execute("handle SIGALRM print nopass") # ignore SIGALRM 6164peda.execute("handle SIGSEGV stop print nopass") # catch SIGSEGV 6165