diff --git a/lib/config.py b/lib/config.py index 5704fc7..d50880c 100644 --- a/lib/config.py +++ b/lib/config.py @@ -35,6 +35,7 @@ "autosave" : ("on", "auto saving peda session, e.g: on|off"), "payload" : ("peda-payload-#FILENAME#.txt", "target file to save output of payload command"), "context" : ("register,code,stack", "context display setting, e.g: register, code, stack, all"), + "clearscr" : ("on", "clear screen for each context display"), "verbose" : ("off", "show detail execution of commands, e.g: on|off"), "debug" : ("off", "show detail error of peda commands, e.g: on|off"), "_teefd" : ("", "internal use only for tracelog/crashlog writing") diff --git a/lib/shellcode.py b/lib/shellcode.py index 957e90b..6b87b0d 100644 --- a/lib/shellcode.py +++ b/lib/shellcode.py @@ -21,7 +21,7 @@ import config from utils import msg, error_msg -if sys.version_info.major is 3: +if sys.version_info.major == 3: from urllib.request import urlopen from urllib.parse import urlencode pyversion = 3 @@ -356,7 +356,7 @@ def display(self, shellcodeId): try: s.request("GET", "/shellcode/files/shellcode-"+str(shellcodeId)+".php") res = s.getresponse() - data = res.read().split("
")[1].split("")[0]
+            data = res.read().decode('utf-8').split("
")[1].split("")[0]
         except:
             error_msg("Failed to download shellcode from shell-storm.org")
             return None
@@ -376,7 +376,7 @@ def zsc(self,os,job,encode):
                     'job': job,
                     'encode': encode})
             shellcode = urlopen("http://api.z3r0d4y.com/index.py?%s\n"%(str(params))).read()
-            if pyversion is 3:
+            if pyversion == 3:
                 shellcode = str(shellcode,encoding='ascii')
             return '\n"'+shellcode.replace('\n','')+'"\n'
         except:
diff --git a/lib/skeleton.py b/lib/skeleton.py
index 123210f..20c30c5 100644
--- a/lib/skeleton.py
+++ b/lib/skeleton.py
@@ -150,7 +150,7 @@ def exploit(vuln):
     args = sys.argv[1:]
     resource.setrlimit(resource.RLIMIT_STACK, (-1, -1))
     resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
-    P = Popen(args, stdin=PIPE)
+    P = Popen(args, stdin=PIPE, env=env)
     P.stdin.write(payload + "\\n")
     while True:
         line = sys.stdin.readline()
diff --git a/lib/utils.py b/lib/utils.py
index 767f132..8f2b038 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -147,6 +147,10 @@ def blue(text, attrib=None):
     """Wrapper for colorize(text, 'blue')"""
     return colorize(text, "blue", attrib)
 
+def clearscreen():
+    """Clear terminal screen"""
+    sys.stdout.write("\x1b[2J\x1b[H")
+
 class message(object):
     """
     Generic pretty printer with redirection.
@@ -564,7 +568,7 @@ def cyclic_pattern_charset(charset_type=None):
 
     Args:
         - charset_type: charset type
-            0: basic (0-9A-za-z)
+            0: basic (0-9A-Za-z)
             1: extended (default)
             2: maximum (almost printable chars)
 
@@ -635,7 +639,7 @@ def cyclic_pattern(size=None, start=None, charset_type=None):
         - size: size of generated pattern (Int)
         - start: the start offset of the generated pattern (Int)
         - charset_type: charset type
-            0: basic (0-9A-za-z)
+            0: basic (0-9A-Za-z)
             1: extended (default)
             2: maximum (almost printable chars)
 
diff --git a/peda.py b/peda.py
index e087f90..14a7f5e 100644
--- a/peda.py
+++ b/peda.py
@@ -42,7 +42,7 @@
 import config
 from nasm import *
 
-if sys.version_info.major is 3:
+if sys.version_info.major == 3:
     from urllib.request import urlopen
     from urllib.parse import urlencode
     pyversion = 3
@@ -50,7 +50,7 @@
     from urllib import urlopen
     from urllib import urlencode
     pyversion = 2
-	
+
 REGISTERS = {
     8 : ["al", "ah", "bl", "bh", "cl", "ch", "dl", "dh"],
     16: ["ax", "bx", "cx", "dx"],
@@ -195,8 +195,7 @@ def string_to_argv(self, str):
             str = str.encode('ascii', 'ignore')
         except:
             pass
-        str = decode_string_escape(str)
-        args = shlex.split(str)
+        args = list(map(lambda x: decode_string_escape(x), shlex.split(str.decode())))
         # need more processing here
         for idx, a in enumerate(args):
             a = a.strip(",")
@@ -422,37 +421,8 @@ def getpid(self):
         status = self.get_status()
         if not status or status == "STOPPED":
             return None
-
-        if self.is_target_remote(): # remote target
-            ctx = config.Option.get("context")
-            config.Option.set("context", None)
-            try:
-                out = self.execute_redirect("call getpid()")
-            except:
-                pass
-
-            config.Option.set("context", ctx)
-
-            if out is None:
-                return None
-            else:
-                out = self.execute_redirect("print $")
-                if out:
-                    return to_int(out.split("=")[1])
-                else:
-                    return None
-
-        if self.getos() == "Linux":
-            out = self.execute_redirect('info proc')
-
-        if out is None: # non-Linux or cannot access /proc, fallback
-            out = self.execute_redirect('info program')
-        out = out.splitlines()[0]
-        if "process" in out or "Thread" in out:
-            pid = out.split()[-1].strip(".)")
-            return int(pid)
-        else:
-            return None
+        pid = gdb.selected_inferior().pid
+        return int(pid) if pid else None
 
     def getos(self):
         """
@@ -1195,9 +1165,9 @@ def get_eflags(self):
 
         return flags
 
-    def set_eflags(self, flagname, value=True):
+    def set_eflags(self, flagname, value):
         """
-        Set/clear value of a flag register
+        Set/clear/toggle value of a flag register
 
         Returns:
             - True if success (Bool)
@@ -1217,6 +1187,8 @@ def set_eflags(self, flagname, value=True):
         flags = {"carry": "CF", "parity": "PF", "adjust": "AF", "zero": "ZF", "sign": "SF",
                     "trap": "TF", "interrupt": "IF", "direction": "DF", "overflow": "OF"}
 
+        flagname = flagname.lower()
+
         if flagname not in flags:
             return False
 
@@ -1224,7 +1196,8 @@ def set_eflags(self, flagname, value=True):
         if not eflags:
             return False
 
-        if eflags[flags[flagname]] != value: # switch value
+        # If value doesn't match the current, or we want to toggle, toggle
+        if value is None or eflags[flags[flagname]] != value:
             reg_eflags = self.getreg("eflags")
             reg_eflags ^= eval("EFLAGS_%s" % flags[flagname])
             result = self.execute("set $eflags = 0x%x" % reg_eflags)
@@ -1237,7 +1210,7 @@ def eval_target(self, inst):
         Evaluate target address of an instruction, used for jumpto decision
 
         Args:
-            - inst: AMS instruction text (String)
+            - inst: ASM instruction text (String)
 
         Returns:
             - target address (Int)
@@ -1250,10 +1223,11 @@ def eval_target(self, inst):
         p = re.compile(".*?:\s*[^ ]*\s*(.* PTR ).*(0x[^ ]*)")
         m = p.search(inst)
         if not m:
-            p = re.compile(".*?:\s.*(0x[^ ]*)")
+            p = re.compile(".*?:\s.*\s(0x[^ ]*|\w+)")
             m = p.search(inst)
             if m:
                 target = m.group(1)
+                target = self.parse_and_eval(target)
             else:
                 target = None
         else:
@@ -1450,6 +1424,30 @@ def _get_offline_maps():
 
             return binmap
 
+        def _get_allmaps_osx(pid, remote=False):
+            maps = []
+            #_DATA                 00007fff77975000-00007fff77976000 [    4K] rw-/rw- SM=COW  /usr/lib/system/libremovefile.dylib
+            pattern = re.compile("([^\n]*)\s*  ([0-9a-f][^-\s]*)-([^\s]*) \[.*\]\s([^/]*).*  (.*)")
+
+            if remote: # remote target, not yet supported
+                return maps
+            else: # local target
+                try:  out = execute_external_command("/usr/bin/vmmap -w %s" % self.getpid())
+                except: error_msg("could not read vmmap of process")
+
+            matches = pattern.findall(out)
+            if matches:
+                for (name, start, end, perm, mapname) in matches:
+                    if name.startswith("Stack"):
+                        mapname = "[stack]"
+                    start = to_int("0x%s" % start)
+                    end = to_int("0x%s" % end)
+                    if mapname == "":
+                        mapname = name.strip()
+                    maps += [(start, end, perm, mapname)]
+            return maps
+
+
         def _get_allmaps_freebsd(pid, remote=False):
             maps = []
             mpath = "/proc/%s/map" % pid
@@ -1505,7 +1503,10 @@ def _get_allmaps_linux(pid, remote=False):
         result = []
         pid = self.getpid()
         if not pid: # not running, try to use elfheader()
-            return _get_offline_maps()
+            try:
+                return _get_offline_maps()
+            except:
+                return []
 
         # retrieve all maps
         os   = self.getos()
@@ -1513,7 +1514,8 @@ def _get_allmaps_linux(pid, remote=False):
         maps = []
         try:
             if   os == "FreeBSD": maps = _get_allmaps_freebsd(pid, rmt)
-            elif os == "Linux" :   maps = _get_allmaps_linux(pid, rmt)
+            elif os == "Linux"  : maps = _get_allmaps_linux(pid, rmt)
+            elif os == "Darwin" : maps = _get_allmaps_osx(pid, rmt)
         except Exception as e:
             if config.Option.get("debug") == "on":
                 msg("Exception: %s" %e)
@@ -1632,7 +1634,10 @@ def get_disasm(self, address, count=1):
             - asm code (String)
         """
         code = self.execute_redirect("x/%di 0x%x" % (count, address))
-        return code.rstrip()
+        if code:
+            return code.rstrip()
+        else:
+            return ""
 
     def dumpmem(self, start, end):
         """
@@ -2120,7 +2125,7 @@ def examine_data(value, bits=32):
         return result
 
     @memoized
-    def examine_mem_reference(self, value):
+    def examine_mem_reference(self, value, depth=5):
         """
         Deeply examine a value in memory for its references
 
@@ -2131,8 +2136,16 @@ def examine_mem_reference(self, value):
             - list of tuple of (value(Int), type(String), next_value(Int))
         """
         result = []
+        if depth <= 0:
+            depth = 0xffffffff
+
         (v, t, vn) = self.examine_mem_value(value)
         while vn is not None:
+            if len(result) > depth:
+                _v, _t, _vn = result[-1]
+                result[-1] = (_v, _t, "--> ...")
+                break
+
             result += [(v, t, vn)]
             if v == vn or to_int(v) == to_int(vn): # point to self
                 break
@@ -2226,7 +2239,7 @@ def elfheader(self, name=None):
         if not out:
             return {}
 
-        p = re.compile("\s*(0x[^-]*)->(0x[^ ]*) at (.*):\s*([^ ]*)\s*(.*)")
+        p = re.compile("\s*(0x[^-]*)->(0x[^ ]*) at (0x[^:]*):\s*([^ ]*)\s*(.*)")
         matches = p.findall(out)
 
         for (start, end, offset, hname, attr) in matches:
@@ -3027,7 +3040,7 @@ def _is_running(self):
         """
         pid = peda.getpid()
         if pid is None:
-            text = "not running or target is remote"
+            text = "not running"
             warning_msg(text)
             return None
             #raise Exception(text)
@@ -4358,6 +4371,10 @@ def context(self, *arg):
         if not self._is_running():
             return
 
+        clearscr = config.Option.get("clearscr")
+        if clearscr == "on":
+            clearscreen()
+
         status = peda.get_status()
         # display registers
         if "reg" in opt or "register" in opt:
@@ -4379,6 +4396,26 @@ def context(self, *arg):
 
         return
 
+    def breakrva(self, *arg):
+        """
+        Set breakpoint by Relative Virtual Address (RVA)
+        Usage:
+            MYNAME rva
+            MYNAME rva module_name (e.g binary, shared module name)
+        """
+        (rva, module) = normalize_argv(arg, 2)
+        if rva is None or not to_int(rva):
+            self._missing_argument()
+        if module is None:
+            module = 'binary'
+
+        binmap = peda.get_vmmap(module)
+        if len(binmap) == 0:
+            msg("No module matches '%s'" % module)
+        else:
+            base_address = binmap[0][0]
+            peda.set_breakpoint(base_address+rva)
+        return
 
     #################################
     #   Memory Operation Commands   #
@@ -4708,6 +4745,8 @@ def telescope(self, *arg):
 
         step = peda.intsize()
         if not peda.is_address(address): # cannot determine address
+            msg("Invalid $SP address: 0x%x" % address, "red")
+            return
             for i in range(count):
                 if not peda.execute("x/%sx 0x%x" % ("g" if step == 8 else "w", address + i*step)):
                     break
@@ -4734,10 +4773,10 @@ def telescope(self, *arg):
 
     def eflags(self, *arg):
         """
-        Display/set/clear value of eflags register
+        Display/set/clear/toggle value of eflags register
         Usage:
             MYNAME
-            MYNAME [set|clear] flagname
+            MYNAME [set|clear|toggle] flagname
         """
         FLAGS = ["CF", "PF", "AF", "ZF", "SF", "TF", "IF", "DF", "OF"]
         FLAGS_TEXT = ["Carry", "Parity", "Adjust", "Zero", "Sign", "Trap",
@@ -4747,10 +4786,10 @@ def eflags(self, *arg):
         if not self._is_running():
             return
 
-        if option and not flagname:
+        elif option and not flagname:
             self._missing_argument()
 
-        if option is None: # display eflags
+        elif option is None: # display eflags
             flags = peda.get_eflags()
             text = ""
             for (i, f) in enumerate(FLAGS):
@@ -4762,14 +4801,17 @@ def eflags(self, *arg):
             eflags = peda.getreg("eflags")
             msg("%s: 0x%x (%s)" % (green("EFLAGS"), eflags, text.strip()))
 
-        if option == "set":
-            peda.set_eflags(flagname.lower())
+        elif option == "set":
+            peda.set_eflags(flagname, True)
 
-        if option == "clear":
+        elif option == "clear":
             peda.set_eflags(flagname, False)
 
+        elif option == "toggle":
+            peda.set_eflags(flagname, None)
+
         return
-    eflags.options = ["set", "clear"]
+    eflags.options = ["set", "clear", "toggle"]
 
     def xinfo(self, *arg):
         """
@@ -4802,17 +4844,9 @@ def get_reg_text(r, v):
                 for r in REGISTERS[bits]:
                     if r in regs:
                         text += get_reg_text(r, regs[r])
-                        # text += green("%s" % r.upper().ljust(3)) + ": "
-                        # chain = peda.examine_mem_reference(regs[r])
-                        # text += format_reference_chain(chain)
-                        # text += "\n"
             else:
                 for (r, v) in sorted(regs.items()):
                     text += get_reg_text(r, v)
-                    # text += green("%s" % r.upper().ljust(3)) + ": "
-                    # chain = peda.examine_mem_reference(v)
-                    # text += format_reference_chain(chain)
-                    # text += "\n"
             if text:
                 msg(text.strip())
             if regname is None or "eflags" in regname:
@@ -4823,7 +4857,7 @@ def get_reg_text(r, v):
             warning_msg("not a register nor an address")
         else:
             # Address
-            chain = peda.examine_mem_reference(address)
+            chain = peda.examine_mem_reference(address, depth=0)
             text += format_reference_chain(chain) + "\n"
             vmrange = peda.get_vmrange(address)
             if vmrange:
@@ -5656,7 +5690,7 @@ def shellcode(self, *arg):
             MYNAME generate [arch/]platform type [port] [host]
             MYNAME search keyword (use % for any character wildcard)
             MYNAME display shellcodeId (shellcodeId as appears in search results)
-	    MYNAME zsc [generate customize shellcode] 
+	    MYNAME zsc [generate customize shellcode]
 
             For generate option:
                 default port for bindport shellcode: 16706 (0x4142)
@@ -5755,20 +5789,20 @@ def list_shellcode():
                 while True:
                     for os in oslist:
                         msg('%s %s'%(yellow('[+]'),green(os)))
-                    if pyversion is 2:
+                    if pyversion == 2:
                         os = input('%s'%blue('os:'))
-                    if pyversion is 3:
+                    if pyversion == 3:
                         os = input('%s'%blue('os:'))
-                    if os in oslist: #check if os exist 
+                    if os in oslist: #check if os exist
                         break
                     else:
                         warning_msg("Wrong input! Try Again.")
                 while True:
                     for job in joblist:
                         msg('%s %s'%(yellow('[+]'),green(job)))
-                    if pyversion is 2:
+                    if pyversion == 2:
                         job = raw_input('%s'%blue('job:'))
-                    if pyversion is 3:
+                    if pyversion == 3:
                         job = input('%s'%blue('job:'))
                     if job != '':
                         break
@@ -5777,9 +5811,9 @@ def list_shellcode():
                 while True:
                     for encode in encodelist:
                         msg('%s %s'%(yellow('[+]'),green(encode)))
-                    if pyversion is 2:
+                    if pyversion == 2:
                         encode = raw_input('%s'%blue('encode:'))
-                    if pyversion is 3:
+                    if pyversion == 3:
                         encode = input('%s'%blue('encode:'))
                     if encode != '':
                         break
@@ -6111,6 +6145,7 @@ def sigint_handler(signal, frame):
 Alias("stack", "peda telescope $sp")
 Alias("viewmem", "peda telescope")
 Alias("reg", "peda xinfo register")
+Alias("brva", "breakrva")
 
 # misc gdb settings
 peda.execute("set confirm off")
diff --git a/python23-compatibility.md b/python23-compatibility.md
index e5b0036..d9b05a0 100644
--- a/python23-compatibility.md
+++ b/python23-compatibility.md
@@ -34,7 +34,7 @@ isinstance(x, six.integer_types)
 ## Strings
 
 In Python 2, `bytes` is an alias for `str`. In Python 3, `str` is a unicode
-type and `bytes` is used for a sequece of arbitrary bytes. Use a leading 'b' to
+type and `bytes` is used for a sequence of arbitrary bytes. Use a leading 'b' to
 signify that a string is a `bytes` object.
 
 ```python