-
-
[原创]Linux系统机制学习
-
发表于: 1天前 74
-
最近在学习Linux安全,有了解到一些Linux的机制,与Windows还是有很大的不同的,所以记录一下。
在Windows中一个正在运行的文件是删除不了的,会提示该文件正在被占用。但是Linux确可以,这是因为在 Linux 中,文件系统使用 ”引用计数(reference counting)“ 机制:
直到引用计数降为 0,磁盘空间才会真正回收
可以看到我这里把正在运行的1.sh给删除了,但是查看进程发现/bin/bash还在运行1.sh脚本。

可以通过进程PID定位被删除的文件,由于这里执行1.sh的进程是/bin/bash,我们并没有删除该进程,所以显示还有。如果执行的是elf二进制文件,并且删除了的话,那么这里会显示对应的二进制文件并且标记deleted(标记说明原始路径的文件已被删除,但这个符号链接仍然指向内存中的 inode)

还可以 查看所有打开的文件描述符,这里可以看到1.sh被被叫删除

可以通过 cp 命令恢复被删除的文件。

查看内存映射

查看加载了哪些共享库,即使被删除也能看到,恶意 .so 注入常常会把.so文件删除。

查看进程信息。

使用 lsof 批量发现已删除文件

在Linux中攻击者甚至可以完全不落盘,使用 memfd_create 在内存中创建匿名文件并执行,类似于Windows的内存执行,也可以用上面的命令去查看和恢复。
/proc 是一个虚拟文件系统,它不存在于磁盘上,而是内核在内存中动态生成的"窗口",让你能查看进程的内部状态,这也就解释了为什么上面的命令都是对/proc进行操作,常见 /proc/[pid]/ 目录。

上面我们说过在Linux 中,文件系统使用 ”引用计数“ 机制。这里我举个例子说明一下,想象图书馆里有一本实体书:
实体书(真正的内容) ↑ ├── 索引卡片A:"《Linux安全》→ 3楼B架" (文件名1) ├── 索引卡片B:"《操作系统》→ 3楼B架" (文件名2,硬链接) └── 张三正在手里拿着读 (进程持有fd)
1、撕掉索引卡片A(unlink)只是目录里找不到这个名字了,但书还在架子上,张三还在读。
2、撕掉索引卡片B:最后一张卡片也没了,但张三手里还拿着,书依然不能销毁。
3、张三把书还了(进程关闭fd):卡片为0 + 没人拿着 = 书真正被销毁(磁盘空间回收)。
用专业属于来讲就是删除文件(unlink)只是移除了目录项(dentry),即文件名到 inode 的映射。只要还有进程持有该文件的文件描述符(fd)或正在执行(execute)该文件,inode 和数据块不会被释放直到引用计数降为 0,磁盘空间才会真正回收。关键是要理解文件名 ≠ 文件,文件名只是一个"指针",真正的文件是 inode + 数据块。
Linux内核决定何时释放磁盘空间,看的是两个计数器是否同时为 0。
inode 12345 ┌───────────────────────┐ │ i_nlink(硬链接计数) │ ← 有多少个文件名指向它 │ 当前值: 1 │ ├───────────────────────┤ │ i_count(引用计数) │ ← 有多少个进程正在使用它 │ 当前值: 0 │ └───────────────────────┘ 释放条件:i_nlink == 0 AND i_count == 0
查看文件的硬链接数,其中数字1表示一个硬链接。

此时创建一个硬链接再查看,便有两个硬链接。

ELF 是最常见的二进制格式,Linux 支持多种可执行格式。
(1)ELF是编译型语言(C/C++/Go/Rust)产出的标准格式,对应 Windows 的 PE 格式。
(2) 脚本文件,通过 Shebang 机制。内核执行 /tmp/test.sh 的过程:
1. 内核读取文件前两个字节 → 发现是 "#!"
2. 解析第一行 → 得到解释器路径 "/bin/bash"
3. 内核实际执行的是:
/bin/bash /tmp/test.sh
↑ 解释器(ELF) ↑ 脚本(文本)
常见的 shebang:

(3) a.out 远古格式(历史遗留),ELF 之前的 Unix 标准格式,现在已经基本淘汰。
(4)binfmt_misc Linux内核的万能格式扩展,Linux内核允许你注册任意文件格式,并指定用什么程序来执行它。
查看当前注册的格式,遇到python开头的就用python解释器运行。甚至你可以注册exe文件的解释器,然后就可以在Linux中运行exe了。

所以,Linux 的"可执行文件"本质上是一个开放架构,ELF 只是默认的主流格式。
而且,Linux 通过读取文件内容(头部魔数)来判断文件类型,不依赖扩展名。扩展名在 Linux 中只是给人看的备注,内核完全无视它。
这里我把loop改为loop.txt仍可正常执行。

正确的是用file 去查看是什么文件。

Linux 将几乎所有系统资源都抽象为"文件"这个统一接口。你可以用 open()、read()、write()、close() 这套相同的 API 来操作它们。
Linux一共有七种文件类型,用 ls -l 的第一个字符区分:

这个和 Windows 的普通文件一样,没什么特别的。

目录的本质:一个特殊文件,内容是"文件名 → inode号"的映射表。




⑥ 套接字(s)—— 网络通信也是文件

对应 Windows 的快捷方式(.lnk)或符号链接(mklink)。

inode(Index Node,索引节点)是Linux文件系统中用来描述一个文件所有元数据的固定大小的数据结构,每个文件有且仅有一个 inode。
例如下图。

最关键的是文件名和 inode 是分离的,文件名存在目录文件中,目录本质上是一张"映射表":

完整寻址过程:
用户访问 /home/user/document.txt 内核的工作流程: 第1步:从根目录 / 开始 目录 / 的内容: ┌───────┬────────┐ │ home │ 4000 │ ← 找到 home 的 inode └───────┴────────┘ 第2步:读取 inode 4000 → 得到 /home 的数据块 → 读取目录内容 目录 /home/ 的内容: ┌───────┬────────┐ │ user │ 5000 │ ← 找到 user 的 inode └───────┴────────┘ 第3步:读取 inode 5000 → 得到 /home/user/ 的数据块 → 读取目录内容 目录 /home/user/ 的内容: ┌──────────────┬────────┐ │ document.txt │ 12345 │ ← 终于找到目标 inode! └──────────────┴────────┘ 第4步:读取 inode 12345 → 得到数据块指针 → 读取文件内容 inode 12345: 大小: 4096 数据块: [Block 100] → 读取 Block 100 → 得到 "Hello World"
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。