How to pass SIGINT to child process with Python subprocess.Popen() using shell = true

问题描述 投票:0回答:4

我目前正在尝试编写(Python 2.7.3)一种 GDB 包装器,这将允许我从脚本输入动态切换到与 GDB 的交互式通信。

到目前为止我用

self.process = subprocess.Popen(["gdb vuln"], stdin = subprocess.PIPE,  shell = True)

在我的脚本中启动 gdb。 (

vuln
是我要检查的二进制文件)

由于 gdb 的一个关键特性是暂停附加进程的执行并允许用户在接收到 SIGINT (STRG+C) 时检查寄存器和内存,我确实需要一些方法来将 SIGINT 信号传递给它。

都不是

self.process.send_signal(signal.SIGINT)

也不

os.kill(self.process.pid, signal.SIGINT)

os.killpg(self.process.pid, signal.SIGINT)

为我工作。

当我使用这些功能之一时,没有任何反应。我想这个问题是由

shell=True
的使用引起的。但是,在这一点上我真的没主意了。 这次连我的老朋友谷歌都帮不了我,所以也许你能帮我。提前致谢。

干杯,迈克

python gdb subprocess popen sigint
4个回答
6
投票

这对我有用:

import signal
import subprocess

try:
    p = subprocess.Popen(...)
    p.wait()
except KeyboardInterrupt:
    p.send_signal(signal.SIGINT)
    p.wait()

1
投票

我深入研究了这个问题,发现了一些有趣的事情。也许这些发现会在未来帮助某人。

当使用 suprocess.Popen() 调用

gdb vuln
时,它实际上创建了三个进程,其中返回的 pid 是
sh
(5180) 之一。

ps -a
 5180 pts/0    00:00:00 sh
 5181 pts/0    00:00:00 gdb
 5183 pts/0    00:00:00 vuln

因此向进程发送 SIGINT 实际上会将 SIGINT 发送到

sh
.

此外,我继续寻找答案并偶然发现了这篇文章 https://bugzilla.kernel.org/show_bug.cgi?id=9039

简而言之,这里提到的是:

在定期使用 gdb 时按 STRG+C 时,SIGINT 实际上会发送到被检查的程序(在本例中为

vuln
),然后 ptrace 将拦截它并将其传递给 gdb。 这意味着,如果我使用
self.process.send_signal(signal.SIGINT)
它实际上永远不会以这种方式到达 gdb。

临时解决方法:

我设法通过简单地调用

subprocess.popen()
来解决这个问题,如下所示:

subprocess.Popen("killall -s INT " + self.binary, shell = True)

这只不过是第一个解决方法。当运行多个同名应用程序时,可能会造成严重损害。此外,如果未设置

shell=True
,它会以某种方式失败。 如果有人有更好的解决方法(例如,如何通过 gdb 获取进程启动的 pid),请告诉我。

干杯,迈克

编辑:

感谢Mark指出查看进程的ppid。 我设法使用以下方法缩小了 SIGINT 发送到的进程的范围:

out = subprocess.check_output(['ps', '-Aefj'])
for line in out.splitlines():
    if self.binary in line:
        l = line.split(" ")
        while "" in l:
            l.remove("")
        # Get sid and pgid of child process (/bin/sh)
        sid = os.getsid(self.process.pid)
        pgid  = os.getpgid(self.process.pid)
        #only true for target process
        if l[4] == str(sid) and l[3] != str(pgid):
            os.kill(pid, signal.SIGINT)

0
投票

我过去做过类似下面的事情,如果我记得它似乎对我有用:

def detach_procesGroup():
    os.setpgrp()

subprocess.Popen(command,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE,
                 preexec_fn=detach_processGroup)

0
投票

@sirex 答案的修改,对我来说效果更好:

import signal
import subprocess

p = subprocess.Popen(...)
while True:
    try:
        p.wait()
        exit(0)
    except KeyboardInterrupt:
        p.send_signal(signal.SIGINT)

原始代码将在第 2 次后中止

ctrl-C
.

© www.soinside.com 2019 - 2024. All rights reserved.