From 63d107c1aa9c524e2fd84339b84a97f4eed06943 Mon Sep 17 00:00:00 2001 From: Zach Riggle Date: Mon, 15 May 2017 13:42:47 -0500 Subject: [PATCH] Simplify command exception debugging and make stdio work correctly (#251) * Simplify command exception debugging and make stdio work correctly * Make isort happy * Reorganize exception handler, add default case * Fix print statement * Attempt to use ipdb where available * Sort requirements and add ipdb * Only use pwndbg.stdio in the exception handler * Documentation, hook pdb.set_trace() * Do not require ipdb * Remove import loop, fix accidental call, set python print-stack * Use the correct values for print-stack * Use pdb.Pdb for better set_trace() --- pwndbg/__init__.py | 2 +- pwndbg/commands/__init__.py | 15 +++------ pwndbg/events.py | 20 +++++------- pwndbg/exception.py | 65 +++++++++++++++++++++++++++++++++++++ pwndbg/prompt.py | 4 +-- pwndbg/stdio.py | 25 ++------------ requirements.txt | 12 +++---- 7 files changed, 90 insertions(+), 53 deletions(-) create mode 100644 pwndbg/exception.py diff --git a/pwndbg/__init__.py b/pwndbg/__init__.py index 33e6ea416..fc1264850 100755 --- a/pwndbg/__init__.py +++ b/pwndbg/__init__.py @@ -56,6 +56,7 @@ import pwndbg.disasm.sparc import pwndbg.disasm.x86 import pwndbg.dt import pwndbg.elf +import pwndbg.exception import pwndbg.heap import pwndbg.inthook import pwndbg.memory @@ -64,7 +65,6 @@ import pwndbg.proc import pwndbg.prompt import pwndbg.regs import pwndbg.stack -import pwndbg.stdio import pwndbg.typeinfo import pwndbg.version import pwndbg.vmmap diff --git a/pwndbg/commands/__init__.py b/pwndbg/commands/__init__.py index 52e0586cf..02159dd62 100644 --- a/pwndbg/commands/__init__.py +++ b/pwndbg/commands/__init__.py @@ -6,22 +6,19 @@ from __future__ import print_function from __future__ import unicode_literals import functools -import traceback import gdb import pwndbg.chain import pwndbg.color import pwndbg.enhance +import pwndbg.exception import pwndbg.hexdump import pwndbg.memory import pwndbg.regs -import pwndbg.stdio import pwndbg.symbol import pwndbg.ui -debug = True - class _Command(gdb.Command): """Generic command wrapper""" @@ -48,8 +45,7 @@ class _Command(gdb.Command): self.repeat = self.check_repeated(argument, from_tty) return self(*argv) except TypeError: - if debug: - print(traceback.format_exc()) + pwndbg.exception.handle() raise finally: self.repeat = False @@ -88,14 +84,13 @@ class _Command(gdb.Command): def __call__(self, *args, **kwargs): try: - with pwndbg.stdio.stdio: - return self.function(*args, **kwargs) + return self.function(*args, **kwargs) except TypeError as te: - print(te) print('%r: %s' % (self.function.__name__.strip(), self.function.__doc__.strip())) + pwndbg.exception.handle() except Exception: - print(traceback.format_exc()) + pwndbg.exception.handle() class _ParsedCommand(_Command): diff --git a/pwndbg/events.py b/pwndbg/events.py index 5d06a2719..b2d84d9da 100644 --- a/pwndbg/events.py +++ b/pwndbg/events.py @@ -18,7 +18,7 @@ import gdb import pwndbg.color import pwndbg.config -import pwndbg.stdio +import pwndbg.exception debug = pwndbg.config.Parameter('debug-events', False, 'display internal event debugging info') @@ -116,16 +116,14 @@ def connect(func, event_handler, name=''): objfile_cache.add(path) - if pause: return - with pwndbg.stdio.stdio: - try: - func() - except Exception as e: - msg = "Exception during func={}.{} {!r}".format(func.__module__, func.__name__, a) - msg = pwndbg.color.red(msg) - print(msg, file=sys.stderr) - traceback.print_exc() - raise e + if pause: + return + + try: + func() + except Exception as e: + pwndbg.exception.handle() + raise e registered[event_handler].append(caller) event_handler.connect(caller) diff --git a/pwndbg/exception.py b/pwndbg/exception.py new file mode 100644 index 000000000..61c4b39b5 --- /dev/null +++ b/pwndbg/exception.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import functools +import pdb +import sys +import traceback + +import gdb + +import pwndbg.config + +try: + import ipdb as pdb +except ImportError: + pass + +verbose = pwndbg.config.Parameter('exception-verbose', False, 'whether to print a full stacktracefor exceptions raised in Pwndbg commands') +debug = pwndbg.config.Parameter('exception-debugger', False, 'whether to debug exceptions raised in Pwndbg commands') + +def handle(): + """Displays an exception to the user, optionally displaying a full traceback + and spawning an interactive post-moretem debugger. + + Notes: + - ``set exception-verbose on`` enables stack traces. + - ``set exception-debugger on`` enables the post-mortem debugger. + """ + # Display the error + if debug or verbose: + print(traceback.format_exc()) + else: + exc_type, exc_value, exc_traceback = sys.exc_info() + print(exc_type, exc_value) + + # Break into the interactive debugger + if debug: + with pwndbg.stdio.stdio: + pdb.post_mortem() + + + +@functools.wraps(pdb.set_trace) +def set_trace(): + """Enable sane debugging in Pwndbg by switching to the "real" stdio. + """ + debugger = pdb.Pdb(stdin=sys.__stdin__, + stdout=sys.__stdout__, + skip=['pwndbg.stdio', 'pwndbg.exception']) + debugger.set_trace() + +pdb.set_trace = set_trace + +@pwndbg.config.Trigger([verbose, debug]) +def update(): + if verbose or debug: + command = 'set python print-stack full' + else: + command = 'set python print-stack message' + + gdb.execute(command, from_tty=True, to_string=True) diff --git a/pwndbg/prompt.py b/pwndbg/prompt.py index fa13d69ef..e4e326eb2 100644 --- a/pwndbg/prompt.py +++ b/pwndbg/prompt.py @@ -9,7 +9,6 @@ import gdb import pwndbg.events import pwndbg.memoize -import pwndbg.stdio hint_msg = 'Loaded %i commands. Type pwndbg [filter] for a list.' % len(pwndbg.commands._Command.commands) @@ -31,7 +30,6 @@ def prompt_hook(*a): @pwndbg.memoize.reset_on_stop def prompt_hook_on_stop(*a): - with pwndbg.stdio.stdio: - pwndbg.commands.context.context() + pwndbg.commands.context.context() gdb.prompt_hook = prompt_hook diff --git a/pwndbg/stdio.py b/pwndbg/stdio.py index bee365b96..4106da366 100644 --- a/pwndbg/stdio.py +++ b/pwndbg/stdio.py @@ -19,36 +19,17 @@ import gdb import pwndbg.compat -def get(fd, mode): - if pwndbg.compat.python3: - file = io.open(fd, mode=mode, buffering=0) - - kw ={} - if pwndbg.compat.python3: - kw['write_through']=True - - return io.TextIOWrapper(file, **kw) - else: - return codecs.open(fd, mode, 'utf-8') - - class Stdio(object): queue = [] def __enter__(self, *a, **kw): self.queue.append((sys.stdin, sys.stdout, sys.stderr)) - if pwndbg.compat.python3 or True: - sys.stdin = get('/dev/stdin', 'rb') - sys.stdout = get('/dev/stdout', 'wb') - sys.stderr = get('/dev/stderr', 'wb') + sys.stdin = sys.__stdin__ + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ def __exit__(self, *a, **kw): sys.stdin, sys.stdout, sys.stderr = self.queue.pop() stdio = Stdio() - -if False: - sys.stdin = get(0, 'rb') - sys.stdout = get(1, 'wb') - sys.stderr = get(2, 'wb') diff --git a/requirements.txt b/requirements.txt index e0d356644..e57676553 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,11 @@ +future +isort pip -pycparser psutil>=3.1.0 -python-ptrace>=0.8 +pycparser pyelftools -isort -six -future +python-ptrace>=0.8 ROPgadget +six unicorn>=1.0.0 -https://github.com/aquynh/capstone/archive/next.zip#subdirectory=bindings/python \ No newline at end of file +https://github.com/aquynh/capstone/archive/next.zip#subdirectory=bindings/python