From 6eab554adfd2505f4d909797d05bbc12d05d5959 Mon Sep 17 00:00:00 2001 From: Ricky Zhou Date: Sun, 27 Jan 2019 21:49:02 -0800 Subject: [PATCH 1/3] run_in_new_terminal: Prefer tmux/screen over TERM_PROGRAM and x-terminal-emulator. --- pwnlib/util/misc.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pwnlib/util/misc.py b/pwnlib/util/misc.py index e04fe701e..63a997d03 100644 --- a/pwnlib/util/misc.py +++ b/pwnlib/util/misc.py @@ -187,13 +187,13 @@ def run_in_new_terminal(command, terminal = None, args = None): - If ``context.terminal`` is set it will be used. If it is an iterable then ``context.terminal[1:]`` are default arguments. - If a ``pwntools-terminal`` command exists in ``$PATH``, it is used - - If ``$TERM_PROGRAM`` is set, that is used. - - If X11 is detected (by the presence of the ``$DISPLAY`` environment - variable), ``x-terminal-emulator`` is used. - If tmux is detected (by the presence of the ``$TMUX`` environment variable), a new pane will be opened. - If GNU Screen is detected (by the presence of the ``$STY`` environment variable), a new screen will be opened. + - If ``$TERM_PROGRAM`` is set, that is used. + - If X11 is detected (by the presence of the ``$DISPLAY`` environment + variable), ``x-terminal-emulator`` is used. Arguments: command (str): The command to run. @@ -214,18 +214,18 @@ def run_in_new_terminal(command, terminal = None, args = None): elif which('pwntools-terminal'): terminal = 'pwntools-terminal' args = [] - elif 'TERM_PROGRAM' in os.environ: - terminal = os.environ['TERM_PROGRAM'] - args = [] - elif 'DISPLAY' in os.environ and which('x-terminal-emulator'): - terminal = 'x-terminal-emulator' - args = ['-e'] elif 'TMUX' in os.environ and which('tmux'): terminal = 'tmux' args = ['splitw'] elif 'STY' in os.environ and which('screen'): terminal = 'screen' args = ['-t','pwntools-gdb','bash','-c'] + elif 'TERM_PROGRAM' in os.environ: + terminal = os.environ['TERM_PROGRAM'] + args = [] + elif 'DISPLAY' in os.environ and which('x-terminal-emulator'): + terminal = 'x-terminal-emulator' + args = ['-e'] if not terminal: log.error('Could not find a terminal binary to use. Set context.terminal to your terminal.') From e87e5ff6a7cd8b96f849f3425edd7c7a73d8c099 Mon Sep 17 00:00:00 2001 From: Ricky Zhou Date: Sun, 27 Jan 2019 21:51:38 -0800 Subject: [PATCH 2/3] run_in_new_terminal: Kill running commands when the main process exits. Currently, when a gdb window is opened via gdb.debug(), terminating the main pwntools process leaves the gdb window/process around. It is inconvenient to have to close these leftover gdb windows when iterating on an exploit. Add a kill_at_exit parameter to run_in_new_terminal() which attempts to terminate the command at main process exit. This only works for terminals which do not daemonize, or for tmux, which supports returning the command's PID. This change also removes the special case for closing stdin/stdout/stderr on Mac OS X, as the problem no longer reproduces under tmux 2.8. --- pwnlib/util/misc.py | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/pwnlib/util/misc.py b/pwnlib/util/misc.py index 63a997d03..02757229a 100644 --- a/pwnlib/util/misc.py +++ b/pwnlib/util/misc.py @@ -3,12 +3,14 @@ import base64 import errno import os -import platform import re +import signal import socket import stat import string +import subprocess +from pwnlib import atexit from pwnlib.context import context from pwnlib.log import getLogger from pwnlib.util import fiddling @@ -178,8 +180,8 @@ def which(name, all = False): else: return None -def run_in_new_terminal(command, terminal = None, args = None): - """run_in_new_terminal(command, terminal = None) -> None +def run_in_new_terminal(command, terminal = None, args = None, kill_at_exit = True): + """run_in_new_terminal(command, terminal = None, args = None, kill_at_exit = True) -> int Run a command in a new terminal. @@ -195,10 +197,14 @@ def run_in_new_terminal(command, terminal = None, args = None): - If X11 is detected (by the presence of the ``$DISPLAY`` environment variable), ``x-terminal-emulator`` is used. + If `kill_at_exit` is :const:`True`, try to close the command/terminal when the + current process exits. This may not work for all terminal types. + Arguments: command (str): The command to run. terminal (str): Which terminal to use. args (list): Arguments to pass to the terminal + kill_at_exit (bool): Whether to close the command/terminal on process exit. Note: The command is opened with ``/dev/null`` for stdin, stdout, stderr. @@ -216,7 +222,7 @@ def run_in_new_terminal(command, terminal = None, args = None): args = [] elif 'TMUX' in os.environ and which('tmux'): terminal = 'tmux' - args = ['splitw'] + args = ['splitw', '-F' '#{pane_pid}', '-P'] elif 'STY' in os.environ and which('screen'): terminal = 'screen' args = ['-t','pwntools-gdb','bash','-c'] @@ -248,17 +254,23 @@ def run_in_new_terminal(command, terminal = None, args = None): log.debug("Launching a new terminal: %r" % argv) - pid = os.fork() - - if pid == 0: - # Closing the file descriptors makes everything fail under tmux on OSX. - if platform.system() != 'Darwin': - devnull = open(os.devnull, 'rwb') - os.dup2(devnull.fileno(), 0) - os.dup2(devnull.fileno(), 1) - os.dup2(devnull.fileno(), 2) - os.execv(argv[0], argv) - os._exit(1) + stdin = stdout = stderr = open(os.devnull, 'rwb') + if terminal == 'tmux': + stdout = subprocess.PIPE + + p = subprocess.Popen(argv, stdin=stdin, stdout=stdout, stderr=stderr) + + if terminal == 'tmux': + out, _ = p.communicate() + pid = int(out) + else: + pid = p.pid + + if kill_at_exit: + if terminal == 'tmux': + atexit.register(lambda: os.kill(pid, signal.SIGTERM)) + else: + atexit.register(lambda: p.terminate()) return pid From a4998184c617063f6ea0647ecc9a3a5f4cf181cf Mon Sep 17 00:00:00 2001 From: Arusekk Date: Sun, 1 Nov 2020 13:04:17 +0100 Subject: [PATCH 3/3] Fix regression and add CHANGELOG --- CHANGELOG.md | 4 ++++ pwn/toplevel.py | 1 + 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d43bf2e99..367aae89d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,10 @@ The table below shows which release corresponds to each branch, and what date th ## 4.5.0 (`dev`) +- [#1261][1261] Misc `run_in_new_terminal` improvements (notably gdb terminated by default) + +[1261]: https://github.com/Gallopsled/pwntools/pull/1261 + ## 4.4.0 (`beta`) - [#1541][1541] Use `context.newline` for tubes by default diff --git a/pwn/toplevel.py b/pwn/toplevel.py index 9b253e802..68c984bc1 100644 --- a/pwn/toplevel.py +++ b/pwn/toplevel.py @@ -4,6 +4,7 @@ import math import operator import os +import platform import re import requests import socks