无法使用termios.TIOCSTI伪造终端输入

Bru*_*sky 2 python linux terminal

我见过的大多数代码示例都试图从没有本地回声的stdin读取.为此,他们修改"本地模式"标志以将设置删除为"回声输入字符".我想我可以修改"输入模式"标志TIOCSTI,用于"在输入队列中插入给定字节"..但是,即使我以root身份运行脚本,它也没有任何效果.我写给fd的任何东西似乎都转到终端输出,而不是终端输入.基本上我想要做的就是这个确切的事情,但在纯粹的python中.

"""
termfake.py

Usage: sudo python termfake.py /dev/ttys002

Get the tty device path of a different local termimal by running `tty`
in that terminal.
"""

import sys
import termios

fd = open(sys.argv[1], 'w')
fdno = fd.fileno()

# Returns [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
tatters = termios.tcgetattr(fdno)
print('original', tatters)
tatters[0] = termios.TIOCSTI
print('TIOCSTI', termios.TIOCSTI)

# Set iflag
termios.tcsetattr(fdno, termios.TCSANOW, tatters)

# Verify setting change
with open('/dev/ttys002', 'w') as fd2:
    print('modified', termios.tcgetattr(fd2.fileno()))

fd.write('This is test\n')
fd.close()
Run Code Online (Sandbox Code Playgroud)

Ulf*_*zer 6

TIOCSTI是一个ioctl(记录在案tty_ioctl(4)),而不是终端设置,所以你不能使用tcsetattr()- 你需要输入假输入的每个字符ioctl().从来没有必要从Python做过ioctl,但是以下似乎适用于ls在运行Bash 的不同终端(指定为参数,例如/ dev/pts/13)中运行:

import fcntl
import sys
import termios

with open(sys.argv[1], 'w') as fd:
    for c in "ls\n":
        fcntl.ioctl(fd, termios.TIOCSTI, c)
Run Code Online (Sandbox Code Playgroud)

TIOCSTI需要root权限(或CAP_SYS_ADMIN更具体,但实际上通常在实践中是相同的) - 请参阅capabilities(7).


Bru*_*sky 6

我从@Ulfalizer 那里得到了答案,并将其扩展为一个完整且可用的应用程序。

import sys
import fcntl
import termios
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('tty', type=argparse.FileType('w'),
                    help='full tty path as given by the tty command')
group = parser.add_mutually_exclusive_group()
group.add_argument('-n', action='store_true',
                   help='prevent sending a trailing newline character')
group.add_argument('--stdin', action='store_true',
                   help='read input from stdin')
group = parser.add_argument_group()
group.add_argument('cmd', nargs='?',
                    help='command to run (required if not using --stdin)')
group.add_argument('args', nargs='*',
                    help='arguments to command')
args = parser.parse_known_args()

if args.stdin:
    data = sys.stdin.read()
else:
    data = ' '.join([args.cmd] + args.args)

for c in data:
    fcntl.ioctl(args.tty, termios.TIOCSTI, c)
if not args.n and data[-1][-1] != '\n':
    fcntl.ioctl(args.tty, termios.TIOCSTI, '\n')
Run Code Online (Sandbox Code Playgroud)

以下是使用方法:

1号航站楼:做...

$ tty > /tmp/t1
Run Code Online (Sandbox Code Playgroud)

2号航站楼:做...

$ sudo python termfake.py $(cat /tmp/t1) date +%s
Run Code Online (Sandbox Code Playgroud)

终端#1:观察...

$ tty > /tmp/t1
$ date +%s
1487276400
Run Code Online (Sandbox Code Playgroud)