CTF-All-In-One/doc/1.3_linux_basic.md

303 lines
14 KiB
Markdown
Raw Normal View History

2017-10-11 17:22:24 +07:00
# 1.3 Linux 基础
2017-07-18 11:05:20 +07:00
2017-08-05 19:44:45 +07:00
- [常用基础命令](#常用基础命令)
2017-08-14 21:26:29 +07:00
- [根目录结构](#根目录结构)
2017-08-06 13:52:07 +07:00
- [进程管理](#进程管理)
2017-08-10 22:25:09 +07:00
- [UID 和 GID](#uid-和-gid)
2017-08-06 13:52:07 +07:00
- [权限设置](#权限设置)
2017-08-05 19:44:45 +07:00
- [字节序](#字节序)
- [输入输出](#输入输出)
2017-08-11 22:38:12 +07:00
- [文件描述符](#文件描述符)
2017-08-20 10:26:20 +07:00
- [核心转储](#核心转储)
2017-09-27 21:27:04 +07:00
- [调用约定](#调用约定)
2017-07-18 23:05:44 +07:00
2017-08-05 19:44:45 +07:00
## 常用基础命令
2017-07-18 11:05:20 +07:00
```text
ls 用来显示目标列表
cd [path] 用来切换工作目录
pwd 以绝对路径的方式显示用户当前工作目录
man [command] 查看Linux中的指令帮助、配置文件帮助和编程帮助等信息
apropos [whatever] 在一些特定的包含系统命令的简短描述的数据库文件里查找关键字
cat [file] 连接文件并打印到标准输出设备上
less [file] 允许用户向前或向后浏览文字档案的内容
mv [file1] [file2] 用来对文件或目录重新命名,或者将文件从一个目录移到另一个目录中
cp [file1] [file2] 用来将一个或多个源文件或者目录复制到指定的目的文件或目录
rm [file] 可以删除一个目录中的一个或多个文件或目录,也可以将某个目录及其下属的所有文件及其子目录均删除掉
nano / vim / emacs 字符终端的文本编辑器
```
```text
管道命令符 "|" 将一个命令的标准输出作为另一个命令的标准输入
```
2017-07-18 23:05:44 +07:00
2017-08-14 21:26:29 +07:00
## 根目录结构
```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`:启动 Linux 的核心文件。
- `/dev`:设备文件。
- `/etc`:存放各种配置文件。
- `/home`:普通用户的主目录。
- `/lib`、`/lib64`:链接到 `/usr/lib`,存放系统及软件需要的动态链接库。
- `/mnt`:这个目录让用户可以临时挂载其他的文件系统。
- `/proc`:虚拟的目录,是系统内存的映射。可直接访问这个目录来获取系统信息。
- `/root`:系统管理员的主目录。
- `tmp`:公用的临时文件存放目录。
- `usr`:应用程序和文件几乎都在这个目录下。
2017-08-06 13:52:07 +07:00
## 进程管理
- top
- 可以实时动态地查看系统的整体运行情况。
- ps
- 用于报告当前系统的进程状态。可以搭配 kill 指令随时中断、删除不必要的程序。
- 查看某进程的状态:`$ ps -aux | grep [file]`其中返回内容最左边的数字为进程号PID
- kill
- 用来删除执行中的程序或工作。
- 删除进程某 PID 指定的进程:`$ kill [PID]`
2017-08-10 22:25:09 +07:00
## 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 用户了。
2017-08-06 13:52:07 +07:00
## 权限设置
 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]`:制定其他人权限为可读、可写和可执行
2017-08-05 19:44:45 +07:00
## 字节序
2017-07-18 23:05:44 +07:00
目前计算机中采用两种字节存储机制大端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)
2017-08-05 19:44:45 +07:00
2017-09-09 22:53:40 +07:00
我们在内存中实际地看一下,在地址 `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"
```
2017-08-05 19:44:45 +07:00
## 输入输出
- 使用命令的输出作为可执行文件的输入参数
- ```$ ./vulnerable 'your_command_here'```
- ```$ ./vulnerable $(your_command_here)```
- 使用命令作为输入
- ```$ your_command_here | ./vulnerable```
- 将命令行输出写入文件
- ```$ your_command_here > filename```
- 使用文件作为输入
- ```$ ./vulnerable < filename```
2017-08-11 22:38:12 +07:00
## 文件描述符
在 Linux 系统中一切皆可以看成是文件文件又分为普通文件、目录文件、链接文件和设备文件。文件描述符file descriptor是内核管理已被打开的文件所创建的索引使用一个非负整数来指代被打开的文件。
标准文件描述符如下:
文件描述符 | 用途 | stdio 流
--- | --- | ---
0 | 标准输入 | stdin
1 | 标准输出 | stdout
2 | 标准错误 | stderr
当一个程序使用 `fork()` 生成一个子进程后,子进程会继承父进程所打开的文件表,此时,父子进程使用同一个文件表,这可能导致一些安全问题。如果使用 `vfork()`,子进程虽然运行于父进程的空间,但拥有自己的进程表项。
2017-08-20 10:26:20 +07:00
## 核心转储
当程序运行的过程中异常终止或崩溃操作系统会将程序当时的内存、寄存器状态、堆栈指针、内存管理信息等记录下来保存在一个文件中这种行为就叫做核心转储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
```
2017-09-27 21:27:04 +07:00
## 调用约定
函数调用约定是对函数调用时如何传递参数的一种约定。关于它的约定有许多种,下面我们分别从内核接口和用户接口介绍 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 参数,则后面的参数在栈上传递。