我编写了这个简单的C程序来解释具有相同特征的更难的问题.

#include <stdio.h>

int main(int argc, char *argv[])
{
    int n;
    while (1){
        scanf("%d", &n);
        printf("%d\n", n);
    }
    return 0;
}

而且它的工作情况与预期一致.

我还编写了一个子流程脚本来与该程序交互:

from subprocess import Popen, PIPE, STDOUT

process = Popen("./a.out", stdin=PIPE, stdout=PIPE, stderr=STDOUT)

# sending a byte
process.stdin.write(b'3')
process.stdin.flush()

# reading the echo of the number
print(process.stdout.readline())

process.stdin.close()

问题是,如果我运行我的python脚本,readline()上的执行就会冻结.事实上,如果我中断脚本,我会得到:

/tmp » python script.py
^CTraceback (most recent call last):
  File "/tmp/script.py", line 10, in <module>
    print(process.stdout.readline())
          ^^^^^^^^^^^^^^^^^^^^^^^^^
KeyboardInterrupt

如果我修改我的python脚本:

from subprocess import Popen, PIPE, STDOUT

process = Popen("./a.out", stdin=PIPE, stdout=PIPE, stderr=STDOUT)

with process.stdin as pipe:
    pipe.write(b"3")
    pipe.flush()

# reading the echo of the number
print(process.stdout.readline())

# sending another num:
pipe.write(b"4")
pipe.flush()

process.stdin.close()

我得到了以下输出:

» python script.py
b'3\n'
Traceback (most recent call last):
  File "/tmp/script.py", line 13, in <module>
    pipe.write(b"4")
ValueError: write to closed file

这意味着第一个输入被正确发送,并且读取也被完成.

我真的找不到任何东西来解释这种行为;有人能帮我理解吗? 提前谢谢你

[EDIT]:因为有很多点需要澄清,我添加了这个编辑.我正在使用rop技术训练如何利用缓冲区溢出漏洞,我正在编写一个Python脚本来实现这一点.为了利用这个漏洞,由于ASLR,我需要发现libc地址,并使程序重新启动而不终止.由于脚本将在目标机器上执行,我不知道哪些库可用,那么我将使用subprocess,因为它是内置在python中的.在不深入细节的情况下,攻击在第一个scanf上发送一个序列bytes,目的是泄漏libc基址并重新启动程序;然后发送第二个有效载荷以获得一个shell,我将在交互模式下与之通信.

这就是为什么:

  1. 我只能使用内置库
  2. 我必须发送字节并且不能追加结尾\n:我的有效负载将不对齐或可能利兹失败
  3. 我需要保持开放的stdin开放
  4. 我不能更改C代码

推荐答案

更改这些选项:

  • 在C程序读取的数字之间发送分隔符.Scanf(3)接受任何非数字字节作为分隔符.为了获得最简单的缓冲,可以从Python中发送换行符(例如.write(b'42\n')).如果没有分隔符,scanf(3)将无限期地等待更多的数字.

  • 在每次写入(使用C和Python语言)之后,刷新输出.

这对我很管用:

#include <stdio.h>

int main(int argc, char *argv[])
{
    int n;
    while (1){
        scanf("%d", &n);
        printf("%d\n", n);
        fflush(stdout);  /* I've added this line only. */
    }
    return 0;
}
import subprocess

p = subprocess.Popen(
    ('./a.out',), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
try:
  print('A'); p.stdin.write(b'42 '); p.stdin.flush()
  print('B'); print(repr(p.stdout.readline()));
  print('C'); p.stdin.write(b'43\n'); p.stdin.flush()
  print('D'); print(repr(p.stdout.readline()));
finally:
  print('E'); print(p.kill())

当您的原始C程序在终端窗口中交互运行时能够正常运行的原因是,在C语言中,当向终端写入换行符(\n)时,会自动刷新输出.因此,printf("%d\n", n);最终会执行隐式fflush(stdout);.

当您的原始C程序在从带有subprocess的Python运行时不能工作的原因是,它将其输出写入管道(而不是终端),并且没有自动刷新到管道.发生的情况是,Python程序正在等待字节,而C程序不会将这些字节写入管道,但它正在等待更多字节(在下一个scanf中),因此两个程序都在无限期地等待对方.(但是,在几KiB(通常为8192字节)的输出之后会有部分自动刷新.但一个十进制数字太短了,不能触发这一点.)

如果无法更改C程序,那么应该使用终端设备而不是管道来进行C和Python程序之间的通信.pty Python模块可以创建终端设备,这对我来说与你原来的C程序:

import os, pty, subprocess

master_fd, slave_fd = pty.openpty()
p = subprocess.Popen(
    ('./a.out',), stdin=slave_fd, stdout=slave_fd,
    preexec_fn=lambda: os.close(master_fd))
try:
  os.close(slave_fd)
  master = os.fdopen(master_fd, 'rb+', buffering=0)
  print('A'); master.write(b'42\n'); master.flush()
  print('B'); print(repr(master.readline()));
  print('C'); master.write(b'43\n'); master.flush()
  print('D'); print(repr(master.readline()));
finally:
  print('E'); print(p.kill())

如果你不想从Python发送换行符,这里有一个没有换行符的解决方案,它对我来说很有效:

import os, pty, subprocess, termios

master_fd, slave_fd = pty.openpty()
ts = termios.tcgetattr(master_fd)
ts[3] &= ~(termios.ICANON | termios.ECHO)
termios.tcsetattr(master_fd, termios.TCSANOW, ts)
p = subprocess.Popen(
    ('./a.out',), stdin=slave_fd, stdout=slave_fd,
    preexec_fn=lambda: os.close(master_fd))
try:
  os.close(slave_fd)
  master = os.fdopen(master_fd, 'rb+', buffering=0)
  print('A'); master.write(b'42 '); master.flush()
  print('B'); print(repr(master.readline()));
  print('C'); master.write(b'43\t'); master.flush()
  print('D'); print(repr(master.readline()));
finally:
  print('E'); print(p.kill())

Python相关问答推荐

使用scipy. optimate.least_squares()用可变数量的参数匹配两条曲线

acme错误-Veritas错误:模块收件箱没有属性linear_util'

如何使用matplotlib在Python中使用规范化数据和原始t测试值创建组合热图?

如何将双框框列中的成对变成两个新列

处理带有间隙(空)的duckDB上的重复副本并有效填充它们

实现自定义QWidgets作为QTimeEdit的弹出窗口

索引到 torch 张量,沿轴具有可变长度索引

在两极中过滤

Python—转换日期:价目表到新行

如何使用OpenGL使球体遵循Python中的八样路径?

为什么我的sundaram筛这么低效

通过追加列表以极向聚合

使用字典或列表的值组合

处理Gekko的非最优解

如何在Python中将超链接添加到PDF中每个页面的顶部?

当HTTP 201响应包含 Big Data 的POST请求时,应该是什么?  

如何获取包含`try`外部堆栈的`__traceback__`属性的异常

如何提高Pandas DataFrame中随机列 Select 和分配的效率?

Django抛出重复的键值违反唯一约束错误

使用xlsxWriter在EXCEL中为数据帧的各行上色