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