CTF-All-In-One/doc/1.3_linux_basic.md
Komeiji DrimTuer 10bbc07cb9
完善LD_PERLOAD中关于interpreter的描述
添加了具体的失败原因。关于ArchLinux版本过旧的描述来自我的实践。
2018-05-16 00:57:12 +08:00

693 lines
28 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 1.3 Linux 基础
- [常用基础命令](#常用基础命令)
- [Bash 快捷键](#bash-快捷键)
- [根目录结构](#根目录结构)
- [进程管理](#进程管理)
- [UID 和 GID](#uid-和-gid)
- [权限设置](#权限设置)
- [字节序](#字节序)
- [输入输出](#输入输出)
- [文件描述符](#文件描述符)
- [核心转储](#核心转储)
- [调用约定](#调用约定)
- [环境变量](#环境变量)
- [/proc/[pid]](#procpid)
## 常用基础命令
```text
ls 用来显示目标列表
cd [path] 用来切换工作目录
pwd 以绝对路径的方式显示用户当前工作目录
man [command] 查看Linux中的指令帮助、配置文件帮助和编程帮助等信息
apropos [whatever] 在一些特定的包含系统命令的简短描述的数据库文件里查找关键字
echo [string] 打印一行文本,参数“-e”可激活转义字符
cat [file] 连接文件并打印到标准输出设备上
less [file] 允许用户向前或向后浏览文字档案的内容
mv [file1] [file2] 用来对文件或目录重新命名,或者将文件从一个目录移到另一个目录中
cp [file1] [file2] 用来将一个或多个源文件或者目录复制到指定的目的文件或目录
rm [file] 可以删除一个目录中的一个或多个文件或目录,也可以将某个目录及其下属的所有文件及其子目录均删除掉
ps 用于报告当前系统的进程状态
top 实时查看系统的整体运行情况
kill 杀死一个进程
ifconfig 查看或设置网络设备
ping 查看网络上的主机是否工作
netstat 显示网络连接、路由表和网络接口信息
nc(netcat) 建立 TCP 和 UDP 连接并监听
su 切换当前用户身份到其他用户身份
touch [file] 创建新的空文件
mkdir [dir] 创建目录
chmod 变更文件或目录的权限
chown 变更某个文件或目录的所有者和所属组
nano / vim / emacs 字符终端的文本编辑器
exit 退出 shell
```
```text
管道命令符 "|" 将一个命令的标准输出作为另一个命令的标准输入
```
使用变量:
```text
var=value 给变量var赋值value
$var, ${var} 取变量的值
`cmd`, $(cmd) 代换标准输出
'string' 非替换字符串
"string" 可替换字符串
```
```text
$ var="test";
$ echo $var
test
$ echo 'This is a $var';
This is a $var
$ echo "This is a $var";
This is a test
$ echo `date`;
2017年 11月 06日 星期一 14:40:07 CST
$ $(bash)
$ echo $0
/bin/bash
$ $($0)
```
## Bash 快捷键
```text
Up(Down) 上(下)一条指令
Ctrl + c 终止当前进程
Ctrl + z 挂起当前进程使用“fg”可唤醒
Ctrl + d 删除光标处的字符
Ctrl + l 清屏
Ctrl + a 移动到命令行首
Ctrl + e 移动到命令行尾
Ctrl + b 按单词后移(向左)
Ctrl + f 按单词前移(向右)
Ctrl + Shift + c 复制
Ctrl + Shift + v 粘贴
```
更多细节请查看https://ss64.com/bash/syntax-keyboard.html
## 根目录结构
```text
$ uname -a
Linux manjaro 4.11.5-1-ARCH #1 SMP PREEMPT Wed Jun 14 16:19:27 CEST 2017 x86_64 GNU/Linux
$ ls -al /
drwxr-xr-x 17 root root 4096 Jun 28 20:17 .
drwxr-xr-x 17 root root 4096 Jun 28 20:17 ..
lrwxrwxrwx 1 root root 7 Jun 21 22:44 bin -> usr/bin
drwxr-xr-x 4 root root 4096 Aug 10 22:50 boot
drwxr-xr-x 20 root root 3140 Aug 11 11:43 dev
drwxr-xr-x 101 root root 4096 Aug 14 13:54 etc
drwxr-xr-x 3 root root 4096 Apr 8 19:59 home
lrwxrwxrwx 1 root root 7 Jun 21 22:44 lib -> usr/lib
lrwxrwxrwx 1 root root 7 Jun 21 22:44 lib64 -> usr/lib
drwx------ 2 root root 16384 Apr 8 19:55 lost+found
drwxr-xr-x 2 root root 4096 Oct 1 2015 mnt
drwxr-xr-x 15 root root 4096 Jul 15 20:10 opt
dr-xr-xr-x 267 root root 0 Aug 3 09:41 proc
drwxr-x--- 9 root root 4096 Jul 22 22:59 root
drwxr-xr-x 26 root root 660 Aug 14 21:08 run
lrwxrwxrwx 1 root root 7 Jun 21 22:44 sbin -> usr/bin
drwxr-xr-x 4 root root 4096 May 28 22:07 srv
dr-xr-xr-x 13 root root 0 Aug 3 09:41 sys
drwxrwxrwt 36 root root 1060 Aug 14 21:27 tmp
drwxr-xr-x 11 root root 4096 Aug 14 13:54 usr
drwxr-xr-x 12 root root 4096 Jun 28 20:17 var
```
由于不同的发行版会有略微的不同,我们这里使用的是基于 Arch 的发行版 Manjaro以上就是根目录下的内容我们介绍几个重要的目录
- `/bin`、`/sbin`:链接到 `/usr/bin`,存放 Linux 一些核心的二进制文件,其包含的命令可在 shell 上运行。
- `/boot`:操作系统启动时要用到的程序。
- `/dev`:包含了所有 Linux 系统中使用的外部设备。需要注意的是这里并不是存放外部设备的驱动程序,而是一个访问这些设备的端口。
- `/etc`:存放系统管理时要用到的各种配置文件和子目录。
- `/etc/rc.d`:存放 Linux 启动和关闭时要用到的脚本。
- `/home`:普通用户的主目录。
- `/lib`、`/lib64`:链接到 `/usr/lib`,存放系统及软件需要的动态链接共享库。
- `/mnt`:这个目录让用户可以临时挂载其他的文件系统。
- `/proc`:虚拟的目录,是系统内存的映射。可直接访问这个目录来获取系统信息。
- `/root`:系统管理员的主目录。
- `/srv`:存放一些服务启动之后需要提取的数据。
- `/sys`:该目录下安装了一个文件系统 sysfs。该文件系统是内核设备树的一个直观反映。当一个内核对象被创建时对应的文件和目录也在内核对象子系统中被创建。
- `/tmp`:公用的临时文件存放目录。
- `/usr`:应用程序和文件几乎都在这个目录下。
- `/usr/src`:内核源代码的存放目录。
- `/var`:存放了很多服务的日志信息。
## 进程管理
- top
- 可以实时动态地查看系统的整体运行情况。
- ps
- 用于报告当前系统的进程状态。可以搭配 kill 指令随时中断、删除不必要的程序。
- 查看某进程的状态:`$ ps -aux | grep [file]`其中返回内容最左边的数字为进程号PID
- kill
- 用来删除执行中的程序或工作。
- 删除进程某 PID 指定的进程:`$ kill [PID]`
## UID 和 GID
Linux 是一个支持多用户的操作系统,每个用户都有 User ID(UID) 和 Group ID(GID)UID 是对一个用户的单一身份标识,而 GID 则对应多个 UID。知道某个用户的 UID 和 GID 是非常有用的,一些程序可能就需要 UID/GID 来运行。可以使用 `id` 命令来查看:
```text
$ id root
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),19(log)
$ id firmy
uid=1000(firmy) gid=1000(firmy) groups=1000(firmy),3(sys),7(lp),10(wheel),90(network),91(video),93(optical),95(storage),96(scanner),98(power),56(bumblebee)
```
UID 为 0 的 root 用户类似于系统管理员,它具有系统的完全访问权。我自己新建的用户 firmy其 UID 为 1000是一个普通用户。GID 的关系存储在 `/etc/group` 文件中:
```text
$ cat /etc/group
root:x:0:root
bin:x:1:root,bin,daemon
daemon:x:2:root,bin,daemon
sys:x:3:root,bin,firmy
......
```
所有用户的信息(除了密码)都保存在 `/etc/passwd` 文件中,而为了安全起见,加密过的用户密码保存在 `/etc/shadow` 文件中,此文件只有 root 权限可以访问。
```text
$ sudo cat /etc/shadow
root:$6$root$wvK.pRXFEH80GYkpiu1tEWYMOueo4tZtq7mYnldiyJBZDMe.mKwt.WIJnehb4bhZchL/93Oe1ok9UwxYf79yR1:17264::::::
firmy:$6$firmy$dhGT.WP91lnpG5/10GfGdj5L1fFVSoYlxwYHQn.llc5eKOvr7J8nqqGdVFKykMUSDNxix5Vh8zbXIapt0oPd8.:17264:0:99999:7:::
```
由于普通用户的权限比较低,这里使用 `sudo` 命令可以让普通用户以 root 用户的身份运行某一命令。使用 `su` 命令则可以切换到一个不同的用户:
```text
$ whoami
firmy
$ su root
# whoami
root
```
`whoami` 用于打印当前有效的用户名称shell 中普通用户以 `$` 开头root 用户以 `#` 开头。在输入密码后,我们已经从 firmy 用户转换到 root 用户了。
## 权限设置
 Linux 中,文件或目录权限的控制分别以读取、写入、执行 3 种一般权限来区分,另有 3 种特殊权限可供运用。
使用 `ls -l [file]` 来查看某文件或目录的信息:
```text
$ ls -l /
lrwxrwxrwx 1 root root 7 Jun 21 22:44 bin -> usr/bin
drwxr-xr-x 4 root root 4096 Jul 28 08:48 boot
-rw-r--r-- 1 root root 18561 Apr 2 22:48 desktopfs-pkgs.txt
```
第一栏从第二个字母开始就是权限字符串,权限表示三个为一组,依次是所有者权限、组权限、其他人权限。每组的顺序均为 `rwx`,如果有相应权限,则表示成相应字母,如果不具有相应权限,则用 `-` 表示。
- `r`:读取权限,数字代号为 “4”
- `w`:写入权限,数字代号为 “2”
- `x`:执行或切换权限,数字代号为 “1”
通过第一栏的第一个字母可知,第一行是一个链接文件 `l`),第二行是个目录(`d`),第三行是个普通文件(`-`)。
用户可以使用 `chmod` 指令去变更文件与目录的权限。权限范围被指定为所有者(`u`)、所属组(`g`)、其他人(`o`)和所有人(`a`)。
- -R递归处理将指令目录下的所有文件及子目录一并处理
- <权限范围>+<权限设置>:开启权限范围的文件或目录的该选项权限设置
- `$ chmod a+r [file]`:赋予所有用户读取权限
- <权限范围>-<权限设置>:关闭权限范围的文件或目录的该选项权限设置
- `$ chmod u-w [file]`:取消所有者写入权限
- <权限范围>=<权限设置>:指定权限范围的文件或目录的该选项权限设置;
- `$ chmod g=x [file]`:指定组权限为可执行
- `$ chmod o=rwx [file]`:制定其他人权限为可读、可写和可执行
![](../pic/1.3_file.png)
## 字节序
目前计算机中采用两种字节存储机制大端Big-endian和小端Little-endian
>MSB (Most Significan Bit/Byte):最重要的位或最重要的字节。
>
>LSB (Least Significan Bit/Byte):最不重要的位或最不重要的字节。
Big-endian 规定 MSB 在存储时放在低地址在传输时放在流的开始LSB 存储时放在高地址在传输时放在流的末尾。Little-endian 则相反。常见的 Intel 处理器使用 Little-endian而 PowerPC 系列处理器则使用 Big-endian另外 TCP/IP 协议和 Java 虚拟机的字节序也是 Big-endian。
例如十六进制整数 0x12345678 存入以 1000H 开始的内存中:
![](../pic/1.3_byte_order.png)
我们在内存中实际地看一下,在地址 `0xffffd584` 处有字符 `1234`,在地址 `0xffffd588` 处有字符 `5678`
```text
gdb-peda$ x/w 0xffffd584
0xffffd584: 0x34333231
gdb-peda$ x/4wb 0xffffd584
0xffffd584: 0x31 0x32 0x33 0x34
gdb-peda$ python print('\x31\x32\x33\x34')
1234
gdb-peda$ x/w 0xffffd588
0xffffd588: 0x38373635
gdb-peda$ x/4wb 0xffffd588
0xffffd588: 0x35 0x36 0x37 0x38
gdb-peda$ python print('\x35\x36\x37\x38')
5678
gdb-peda$ x/2w 0xffffd584
0xffffd584: 0x34333231 0x38373635
gdb-peda$ x/8wb 0xffffd584
0xffffd584: 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38
gdb-peda$ python print('\x31\x32\x33\x34\x35\x35\x36\x37\x38')
123455678
db-peda$ x/s 0xffffd584
0xffffd584: "12345678"
```
## 输入输出
- 使用命令的输出作为可执行文件的输入参数
- `$ ./vulnerable 'your_command_here'`
- `$ ./vulnerable $(your_command_here)`
- 使用命令作为输入
- `$ your_command_here | ./vulnerable`
- 将命令行输出写入文件
- `$ your_command_here > filename`
- 使用文件作为输入
- `$ ./vulnerable < filename`
## 文件描述符
在 Linux 系统中一切皆可以看成是文件文件又分为普通文件、目录文件、链接文件和设备文件。文件描述符file descriptor是内核管理已被打开的文件所创建的索引使用一个非负整数来指代被打开的文件。
标准文件描述符如下:
文件描述符 | 用途 | stdio 流
--- | --- | ---
0 | 标准输入 | stdin
1 | 标准输出 | stdout
2 | 标准错误 | stderr
当一个程序使用 `fork()` 生成一个子进程后,子进程会继承父进程所打开的文件表,此时,父子进程使用同一个文件表,这可能导致一些安全问题。如果使用 `vfork()`,子进程虽然运行于父进程的空间,但拥有自己的进程表项。
## 核心转储
当程序运行的过程中异常终止或崩溃操作系统会将程序当时的内存、寄存器状态、堆栈指针、内存管理信息等记录下来保存在一个文件中这种行为就叫做核心转储Core Dump
#### 会产生核心转储的信号
Signal | Action | Comment
--- | --- | ---
SIGQUIT | Core | Quit from keyboard
SIGILL | Core | Illegal Instruction
SIGABRT | Core | Abort signal from abort
SIGSEGV | Core | Invalid memory reference
SIGTRAP | Core | Trace/breakpoint trap
#### 开启核心转储
- 输入命令 `ulimit -c`,输出结果为 `0`,说明默认是关闭的。
- 输入命令 `ulimit -c unlimited` 即可在当前终端开启核心转储功能。
- 如果想让核心转储功能永久开启,可以修改文件 `/etc/security/limits.conf`,增加一行:
```
#<domain> <type> <item> <value>
* soft core unlimited
```
#### 修改转储文件保存路径
- 通过修改 `/proc/sys/kernel/core_uses_pid`,可以使生成的核心转储文件名变为 `core.[pid]` 的模式。
```
# echo 1 > /proc/sys/kernel/core_uses_pid
```
- 还可以修改 `/proc/sys/kernel/core_pattern` 来控制生成核心转储文件的保存位置和文件名格式。
```
# echo /tmp/core-%e-%p-%t > /proc/sys/kernel/core_pattern
```
此时生成的文件保存在 `/tmp/` 目录下,文件名格式为 `core-[filename]-[pid]-[time]`
#### 使用 gdb 调试核心转储文件
```text
$ gdb [filename] [core file]
```
#### 例子
```text
$ cat core.c
#include <stdio.h>
void main(int argc, char **argv) {
char buf[5];
scanf("%s", buf);
}
$ gcc -m32 -fno-stack-protector core.c
$ ./a.out
AAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
$ file /tmp/core-a.out-12444-1503198911
/tmp/core-a.out-12444-1503198911: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), SVR4-style, from './a.out', real uid: 1000, effective uid: 1000, real gid: 1000, effective gid: 1000, execfn: './a.out', platform: 'i686'
$ gdb a.out /tmp/core-a.out-12444-1503198911 -q
Reading symbols from a.out...(no debugging symbols found)...done.
[New LWP 12444]
Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x5655559b in main ()
gdb-peda$ info frame
Stack level 0, frame at 0x41414141:
eip = 0x5655559b in main; saved eip = <not saved>
Outermost frame: Cannot access memory at address 0x4141413d
Arglist at 0x41414141, args:
Locals at 0x41414141, Previous frame's sp is 0x41414141
Cannot access memory at address 0x4141413d
```
## 调用约定
函数调用约定是对函数调用时如何传递参数的一种约定。关于它的约定有许多种,下面我们分别从内核接口和用户接口介绍 32 位和 64 位 Linux 的调用约定。
#### 内核接口
**x86-32 系统调用约定**Linux 系统调用使用寄存器传递参数。`eax` 为 syscall_number`ebx`、`ecx`、`edx`、`esi`、`ebp` 用于将 6 个参数传递给系统调用。返回值保存在 `eax` 中。所有其他寄存器(包括 EFLAGS都保留在 `int 0x80` 中。
**x86-64 系统调用约定**:内核接口使用的寄存器有:`rdi`、`rsi`、`rdx`、`r10`、`r8`、`r9`。系统调用通过 `syscall` 指令完成。除了 `rcx`、`r11` 和 `rax`,其他的寄存器都被保留。系统调用的编号必须在寄存器 `rax` 中传递。系统调用的参数限制为 6 个,不直接从堆栈上传递任何参数。返回时,`rax` 中包含了系统调用的结果。而且只有 INTEGER 或者 MEMORY 类型的值才会被传递给内核。
#### 用户接口
**x86-32 函数调用约定**:参数通过栈进行传递。最后一个参数第一个被放入栈中,直到所有的参数都放置完毕,然后执行 call 指令。这也是 Linux 上 C 语言函数的方式。
**x86-64 函数调用约定**x86-64 下通过寄存器传递参数,这样做比通过栈有更高的效率。它避免了内存中参数的存取和额外的指令。根据参数类型的不同,会使用寄存器或传参方式。如果参数的类型是 MEMORY则在栈上传递参数。如果类型是 INTEGER则顺序使用 `rdi`、`rsi`、`rdx`、`rcx`、`r8` 和 `r9`。所以如果有多于 6 个的 INTEGER 参数,则后面的参数在栈上传递。
## 环境变量
#### 分类
- 按照生命周期划分
- 永久环境变量:修改相关配置文件,永久生效。
- 临时环境变量:使用 `export` 命令,在当前终端下生效,关闭终端后失效。
- 按照作用域划分
- 系统环境变量:对该系统中所有用户生效。
- 用户环境变量:对特定用户生效。
#### 设置方法
1. 在文件 `/etc/profile` 中添加变量,这种方法对所有用户永久生效。如:
```
# Set our default path
PATH="/usr/local/sbin:/usr/local/bin:/usr/bin"
export PATH
```
添加后执行命令 `source /etc/profile` 使其生效。
2. 在文件 `~/.bash_profile` 中添加变量,这种方法对当前用户永久生效。其余同上。
3. 直接运行命令 `export` 定义变量,这种方法只对当前终端临时生效。
#### 常用变量
使用命令 `echo` 打印变量:
```
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
$ echo $HOME
/home/firmy
$ echo $LOGNAME
firmy
$ echo $HOSTNAME
firmy-pc
$ echo $SHELL
/bin/bash
$ echo $LANG
en_US.UTF-8
```
使用命令 `env` 可以打印出所有环境变量:
```
$ env
```
使用命令 `set` 可以打印处所有本地定义的 shell 变量:
```
$ set
```
使用命令 `unset` 可以清楚环境变量:
```
$ unset $变量名
```
#### LD_PRELOAD
该环境变量可以定义在程序运行前优先加载的动态链接库。在 pwn 题目中,我们可能需要一个特定的 libc这时就可以定义该变量
```
$ LD_PRELOAD=/path/to/libc.so ./binary
```
一个例子:
```
$ ldd /bin/true
linux-vdso.so.1 => (0x00007fff9a9fe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1c083d9000)
/lib64/ld-linux-x86-64.so.2 (0x0000557bcce6c000)
$ LD_PRELOAD=~/libc.so.6 ldd /bin/true
linux-vdso.so.1 => (0x00007ffee55e9000)
/home/firmy/libc.so.6 (0x00007f4a28cfc000)
/lib64/ld-linux-x86-64.so.2 (0x000055f33bc50000)
```
注意,在加载动态链接库时需要使用`ld.so`进行重定位,通常被符号链接到`/lib64/ld-linux-x86-64.so`中。动态链接库在编译时隐式指定`ld.so`的搜索路径并写入ELF Header的INTERP字段中。从其他发行版直接拷贝已编译的`.so`文件可能会引发`ld.so`搜索路径不正确的问题。相似的,在版本依赖高度耦合的发行版中(如ArchLinux),版本相差过大也会引发`ld.so`的运行失败。
本地同版本编译后通常不会出现问题。如果有直接拷贝已编译版本的需要,可以对比`interpreter`确定是否符合要求,但是不保证不会失败。
上面的例子中两个 libc 是这样的:
```
$ file /lib/x86_64-linux-gnu/libc-2.23.so
/lib/x86_64-linux-gnu/libc-2.23.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=088a6e00a1814622219f346b41e775b8dd46c518, for GNU/Linux 2.6.32, stripped
$ file ~/libc.so.6
/home/firmy/libc.so.6: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=088a6e00a1814622219f346b41e775b8dd46c518, for GNU/Linux 2.6.32, stripped
```
都是 `interpreter /lib64/ld-linux-x86-64.so.2`,所以可以替换。
而下面的例子是在 Arch Linux 上使用一个 Ubuntu 的 libc就会出错
```
$ ldd /bin/true
linux-vdso.so.1 (0x00007ffc969df000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f7ddde17000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f7dde3d7000)
$ LD_PRELOAD=~/libc.so.6 ldd /bin/true
Illegal instruction (core dumped)
```
```
$ file /usr/lib/libc-2.26.so
/usr/lib/libc-2.26.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /usr/lib/ld-linux-x86-64.so.2, BuildID[sha1]=458fd9997a454786f071cfe2beb234542c1e871f, for GNU/Linux 3.2.0, not stripped
$ file ~/libc.so.6
/home/firmy/libc.so.6: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=088a6e00a1814622219f346b41e775b8dd46c518, for GNU/Linux 2.6.32, stripped
```
一个在 `interpreter /usr/lib/ld-linux-x86-64.so.2`,而另一个在 `interpreter /lib64/ld-linux-x86-64.so.2`
## /proc/[pid]
proc 文件系统是 Linux 内核提供的,为访问系统内核数据的操作提供接口。在该文件系统下,有一些以数字命名的目录,这些数字是进程的 PID 号,而这些目录是进程目录。
目录下的所有文件如下,然后会介绍几个比较重要的:
```
$ cat - &
[1] 2865
$ ls /proc/2865/
attr cpuset limits ns root statm
autogroup cwd map_files numa_maps sched status
auxv environ maps oom_adj schedstat syscall
cgroup exe mem oom_score setgroups task
clear_refs fd mountinfo oom_score_adj smaps timers
cmdline fdinfo mounts pagemap smaps_rollup timerslack_ns
comm gid_map mountstats personality stack uid_map
coredump_filter io net projid_map stat wchan
[1]+ Stopped cat -
```
#### /proc/[pid]/maps
这个文件大概是最常用的,用于显示进程的内存区域映射信息:
```
$ cat /proc/2865/maps
5580631c6000-5580631ce000 r-xp 00000000 08:01 4981196 /usr/bin/cat
5580633cd000-5580633ce000 r--p 00007000 08:01 4981196 /usr/bin/cat
5580633ce000-5580633cf000 rw-p 00008000 08:01 4981196 /usr/bin/cat
558063c7d000-558063c9e000 rw-p 00000000 00:00 0 [heap]
7f6301cd7000-7f6302027000 r--p 00000000 08:01 4993768 /usr/lib/locale/locale-archive
7f6302027000-7f63021d5000 r-xp 00000000 08:01 4982395 /usr/lib/libc-2.26.so
7f63021d5000-7f63023d5000 ---p 001ae000 08:01 4982395 /usr/lib/libc-2.26.so
7f63023d5000-7f63023d9000 r--p 001ae000 08:01 4982395 /usr/lib/libc-2.26.so
7f63023d9000-7f63023db000 rw-p 001b2000 08:01 4982395 /usr/lib/libc-2.26.so
7f63023db000-7f63023df000 rw-p 00000000 00:00 0
7f63023df000-7f6302404000 r-xp 00000000 08:01 4982398 /usr/lib/ld-2.26.so
7f63025c1000-7f63025c3000 rw-p 00000000 00:00 0
7f63025e1000-7f6302603000 rw-p 00000000 00:00 0
7f6302603000-7f6302604000 r--p 00024000 08:01 4982398 /usr/lib/ld-2.26.so
7f6302604000-7f6302605000 rw-p 00025000 08:01 4982398 /usr/lib/ld-2.26.so
7f6302605000-7f6302606000 rw-p 00000000 00:00 0
7fff2ab81000-7fff2aba2000 rw-p 00000000 00:00 0 [stack]
7fff2abef000-7fff2abf2000 r--p 00000000 00:00 0 [vvar]
7fff2abf2000-7fff2abf4000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
```
#### /proc/[pid]/stack
这个文件表示当前进程的内核调用栈信息:
```
$ sudo cat /proc/2865/stack
[<ffffffffa008d05e>] do_signal_stop+0xae/0x1f0
[<ffffffffa008e50c>] get_signal+0x18c/0x5a0
[<ffffffffa002ac26>] do_signal+0x36/0x610
[<ffffffffa0003019>] exit_to_usermode_loop+0x69/0xa0
[<ffffffffa00038eb>] syscall_return_slowpath+0x9b/0xb0
[<ffffffffa06926e4>] entry_SYSCALL_64_fastpath+0x7b/0x7d
[<ffffffffffffffff>] 0xffffffffffffffff
```
#### /proc/[pid]/auxv
该文件包含了传递给进程的解释器信息,即 auxv(AUXiliary Vector),每一项都是由一个 unsigned long 长度的 ID 加上一个 unsigned long 长度的值构成:
```
$ xxd -e -g8 /proc/2865/auxv
00000000: 0000000000000021 00007fff2abf2000 !........ .*....
00000010: 0000000000000010 00000000bfebfbff ................
00000020: 0000000000000006 0000000000001000 ................
00000030: 0000000000000011 0000000000000064 ........d.......
00000040: 0000000000000003 00005580631c6040 ........@`.c.U..
00000050: 0000000000000004 0000000000000038 ........8.......
00000060: 0000000000000005 0000000000000009 ................
00000070: 0000000000000007 00007f63023df000 ..........=.c...
00000080: 0000000000000008 0000000000000000 ................
00000090: 0000000000000009 00005580631c8290 ...........c.U..
000000a0: 000000000000000b 00000000000003e8 ................
000000b0: 000000000000000c 00000000000003e8 ................
000000c0: 000000000000000d 00000000000003e8 ................
000000d0: 000000000000000e 00000000000003e8 ................
000000e0: 0000000000000017 0000000000000000 ................
000000f0: 0000000000000019 00007fff2ab9ff39 ........9..*....
00000100: 000000000000001a 0000000000000000 ................
00000110: 000000000000001f 00007fff2aba1feb ...........*....
00000120: 000000000000000f 00007fff2ab9ff49 ........I..*....
00000130: 0000000000000000 0000000000000000 ................
```
每个值具体是做什么的,可以用下面的办法显示出来,对比看一看,更详细的可以查看 `/usr/include/elf.h``man ld.so`
```
$ LD_SHOW_AUXV=1 cat -
AT_SYSINFO_EHDR: 0x7fff6afb3000
AT_HWCAP: bfebfbff
AT_PAGESZ: 4096
AT_CLKTCK: 100
AT_PHDR: 0x557b68217040
AT_PHENT: 56
AT_PHNUM: 9
AT_BASE: 0x7f41e5689000
AT_FLAGS: 0x0
AT_ENTRY: 0x557b68219290
AT_UID: 1000
AT_EUID: 1000
AT_GID: 1000
AT_EGID: 1000
AT_SECURE: 0
AT_RANDOM: 0x7fff6aedc0a9
AT_HWCAP2: 0x0
AT_EXECFN: /usr/bin/cat
AT_PLATFORM: x86_64
```
值得一提的是,`AT_SYSINFO_EHDR` 所对应的值是一个叫做的 VDSO(Virtual Dynamic Shared Object) 的地址。在 ret2vdso 漏洞利用方法中会用到参考章节6.1.6)。
#### /proc/[pid]/environ
该文件包含了进程的环境变量:
```
$ strings /proc/2865/environ
```
#### /proc/[pid]/fd
该文件包含了进程打开文件的情况:
```
$ ls -al /proc/2865/fd
total 0
dr-x------ 2 firmy firmy 0 12月 30 11:13 .
dr-xr-xr-x 9 firmy firmy 0 12月 30 11:13 ..
lrwx------ 1 firmy firmy 64 12月 30 12:31 0 -> /dev/pts/2
lrwx------ 1 firmy firmy 64 12月 30 12:31 1 -> /dev/pts/2
lrwx------ 1 firmy firmy 64 12月 30 12:31 2 -> /dev/pts/2
```
#### /proc/[pid]/status
该文件包含了进程的状态信息:
```
$ cat /proc/2865/status
Name: cat
Umask: 0022
State: T (stopped)
Tgid: 2865
Ngid: 0
Pid: 2865
PPid: 2059
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
FDSize: 256
Groups: 3 7 10 56 90 91 93 95 96 98 1000
NStgid: 2865
NSpid: 2865
NSpgid: 2865
NSsid: 2059
VmPeak: 7828 kB
VmSize: 7828 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 788 kB
VmRSS: 788 kB
RssAnon: 64 kB
RssFile: 724 kB
RssShmem: 0 kB
VmData: 312 kB
VmStk: 132 kB
VmExe: 32 kB
VmLib: 1876 kB
VmPTE: 40 kB
VmPMD: 12 kB
VmSwap: 0 kB
HugetlbPages: 0 kB
Threads: 1
SigQ: 2/47723
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 0
Cpus_allowed: ff
Cpus_allowed_list: 0-7
Mems_allowed: 00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 1
nonvoluntary_ctxt_switches: 0
```
#### /proc/[pid]/syscall
该文件包含了进程正在执行的系统调用:
```
$ sudo cat /proc/2865/syscall
0 0x0 0x7f63025e2000 0x20000 0x22 0xffffffffffffffff 0x0 0x7fff2ab9f958 0x7f630210ea11
```
第一个值是系统调用号,后面跟着是六个参数,最后两个值分别是堆栈指针和指令计数器的值。