This commit is contained in:
author
2025-11-08 21:00:06 +08:00
parent 95a4182500
commit fc176f1b7d
1793 changed files with 2099698 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
### 进程和线程内存空间的关系
**进程的内存空间**
一个**进程**可以看作是一个正在运行的程序实例。操作系统为每个进程分配独立的、私有的内存空间,这通常被称为**虚拟地址空间**。这个独立的地址空间主要包括以下几个关键区域:
- **代码段**:存放可执行文件的机器指令(也就是程序的代码)。这部分通常是只读的,以防止程序在运行时修改自身代码
- **数据段**:存放已初始化的全局变量和静态变量
- **BSS 段**:存放未初始化的全局变量和静态变量
- **堆**:用于动态内存分配。程序在运行时,可以随时从堆中申请和释放内存
- **栈**:用于存放局部变量、函数参数和返回地址。栈是自动管理的,遵循先进后出(LIFO)的原则
**关键点**:每个进程都有自己独立的虚拟地址空间。这意味着,一个进程无法直接访问另一个进程的内存,除非使用进程间通信(IPC)机制。这种隔离性是操作系统的核心安全机制,可以防止一个进程的错误影响到其他进程,从而保证系统的稳定性
**线程的内存空间**
一个**线程**是进程内的一个执行单元,有时也被称为“轻量级进程”。与进程不同的是,**同一个进程内的所有线程共享该进程的内存空间**
具体来说,这些线程共享:
- **代码段**:所有线程执行的是同一份程序代码
- **数据段**:所有线程都可以访问和修改同一个进程的全局变量和静态变量
- **堆**:所有线程都可以从同一个堆中申请和释放动态内存
但是,每个线程都有自己**独立的**私有部分:
- **栈**:每个线程都有自己的私有栈,用于存放它自己的局部变量和函数调用信息。这是为了确保每个线程在执行函数时,不会影响到其他线程的执行上下文
- **寄存器**:每个线程都有自己的寄存器集合,用来保存其执行状态
可以把**进程**想象成一个**拥有独立房子的住户**,这个房子里有独立的厨房、客厅和卧室(代表私有的内存空间)。而**线程**则可以想象成**房子里的不同家庭成员**
- **进程**:拥有独立的房子(**独立的地址空间**)
- **线程**:是房子里的家庭成员。他们共享房子里的公共区域,比如**客厅(堆)**、**厨房(数据段)**,但每个人有自己的**卧室(栈)和衣柜(寄存器)**
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
### 介绍下父子进程
**父子进程的创建**
父子进程的关系是通过一个特殊的系统调用——**`fork()`** 来创建的
1. 当一个进程(我们称之为**父进程**)调用 `fork()` 时,操作系统会复制父进程的几乎所有信息,包括代码、数据、堆、栈等,来创建一个新的进程,也就是**子进程**
2. `fork()` 系统调用有一个关键的特点:在父进程中,它会返回新创建的子进程的 **PID(进程ID**;而在子进程中,它会返回 **0**。这使得程序可以根据返回值来判断自己是父进程还是子进程,并执行不同的代码路径
**父子进程的资源关系**
尽管子进程是父进程的副本,但在资源管理上,它们之间存在一些重要的区别:
- **独立的内存空间**:在创建时,子进程会获得一个与父进程几乎相同的独立内存副本。这意味着,父进程和子进程对各自内存的修改不会互相影响。这遵循了 **写时复制(Copy-on-Write, CoW** 的机制,即最初它们共享相同的内存页,只有当任一进程试图修改该内存页时,才会真正创建一个新的副本
- **独立的 PID**:每个进程都有一个唯一的 PID,子进程会获得一个新的、独立的 PID
- **共享资源**:父子进程会共享一些只读资源,例如程序代码段。此外,它们还会共享打开的文件描述符。这意味着,如果在 `fork()` 前父进程打开了一个文件,那么子进程也会继承这个文件描述符,可以继续读写这个文件
**父子进程的生命周期**
- **正常终止**:如果父进程或子进程正常执行完毕,它们会退出。当子进程终止时,它会变成一个**僵尸进程(Zombie Process)**,直到它的父进程通过 `wait()``waitpid()` 系统调用来“收尸”,获取它的退出状态,然后这个僵尸进程才会被操作系统彻底清理
- **孤儿进程**:如果父进程在子进程之前终止,子进程就会成为一个**孤儿进程(Orphan Process)**。在这种情况下,操作系统的 **`init` 进程(PID 1)** 会收养这个孤儿进程,成为它的新父进程。`init` 进程会负责清理这些孤儿进程,防止它们变成永久的僵尸进程
File diff suppressed because it is too large Load Diff
+32
View File
@@ -0,0 +1,32 @@
### 孤儿进程和僵尸进程区别
**僵尸进程**
**僵尸进程**是已经**终止**(执行完毕或被杀死)但其父进程尚未**收集**(`wait()``waitpid()` 系统调用)其退出状态的进程
- **产生原因**:当一个子进程终止时,它会向操作系统发送一个信号,表示自己已完成任务。操作系统会释放该进程几乎所有的资源,包括内存和文件描述符。然而,它的进程描述符(PCBProcess Control Block)仍然保留在内存中,以便父进程能够读取它的退出状态(例如,是否成功执行、退出码是多少)。如果父进程没有及时调用 `wait()``waitpid()` 来获取这些信息并释放子进程的 PCB,子进程就会一直处于“僵尸”状态
- **特点**
- 它**不占用**内存空间
- 它**会占用**一个 PID,因为 PCB 还没有被回收
- 由于 PID 是有限的,大量的僵尸进程可能会导致系统没有可用的 PID,从而无法创建新的进程
- **如何识别**:在 `ps``top` 命令的输出中,僵尸进程的状态通常显示为 `<defunct>``Z`
- **处理方法**:唯一能清理僵尸进程的方法是**杀死它的父进程**。当父进程被杀死后,这个僵尸进程就会变成一个孤儿进程,并被 `init` 进程(PID 1)收养,`init` 进程会负责清理它。
**孤儿进程**
**孤儿进程**是指**正在运行**但其父进程已经**终止**的进程
- **产生原因**:当父进程在子进程之前退出,子进程就成了“孤儿”
- **特点**
- 它**仍然在运行**,只是失去了它的原始父进程
- 它**不会**成为系统的负担,因为它会自动被操作系统中最特殊的进程——`init` 进程(PID 1)收养
- **如何识别**:在 `ps` 命令的输出中,你可以看到它的父进程 ID(PPID)变成了 `1`
- **处理方法**:孤儿进程会被 `init` 进程**收养**。`init` 进程会像对待自己的子进程一样,在它最终终止时调用 `wait()` 来收集其退出状态,从而防止它变成一个僵尸进程
| 特性 | 僵尸进程(Zombie Process | 孤儿进程(Orphan Process |
| -------- | --------------------------------------- | ------------------------------------------ |
| 状态 | 已终止,但其进程描述符未被父进程回收。 | 正在运行,但其父进程已终止。 |
| 产生原因 | 父进程没有调用 wait() 或 waitpid()。 | 父进程在子进程之前退出。 |
| 系统影响 | 占用 PID,大量存在会导致 PID 资源耗尽。 | 通常无害,会被 init 进程收养。 |
| 处理方式 | 杀死其父进程。 | 由 init 进程自动收养并管理,无需人工干预。 |
| ps 状态 | Z 或 <defunct> | 运行中,但其 PPID(父进程 ID)为 1。 |
File diff suppressed because it is too large Load Diff
+36
View File
@@ -0,0 +1,36 @@
### Kill 一个进程的时候都发生了那些事情,从父子进程角度讲
**1. 信号发送与接收**
当你执行 `kill <PID>` 时,操作系统内核会向指定的进程(通常是子进程)发送 `SIGTERM` 信号
- **如果目标进程是子进程**:父进程并没有直接参与这个过程。信号是由内核发送给目标进程的,由内核负责后续的调度和处理
- **如果目标进程是父进程**:同样,信号由内核直接发送。但父进程的死亡会对其子进程产生连锁反应
**2. 进程对信号的响应**
这是最关键的一步,也是 `SIGTERM``SIGKILL` 最大的区别所在
- **接收 `SIGTERM`(友好终止)**
- 这是一个“软性”的终止请求,目标进程可以**捕获(handle)**或**忽略(ignore**这个信号
- 如果目标进程有信号处理程序,它会执行相应的清理工作,例如:
- 释放占用的内存、文件描述符等资源
- 保存当前的工作状态
- 优雅地关闭网络连接
- 清理完成后,进程会正常退出
- **父子进程关系**:如果被 `kill` 的是父进程,并且父进程捕获了 `SIGTERM` 并执行了清理,它可能会在退出前向它的子进程发送信号(例如 `SIGTERM`),或者等待子进程先退出。但如果父进程直接退出,其子进程会变成**孤儿进程**,被 `init` 进程收养
- **接收 `SIGKILL`(强制终止)**
- 这是一个“硬性”的终止请求,目标进程**无法捕获、忽略或阻止**这个信号
- 操作系统内核会直接终止这个进程,**不会**给它任何机会执行清理工作
- 这种方式非常粗暴,可能导致数据丢失、资源泄露(例如,没有关闭的文件、没有释放的锁),所以通常不建议作为首选
- **父子进程关系**
- 如果被 `kill -9` 的是**子进程**,它会立即被内核终止。子进程会进入**僵尸状态**,其父进程需要调用 `wait()` 来收集它的退出状态,否则它会一直占用一个 PID
- 如果被 `kill -9` 的是**父进程**,父进程会立即被内核强制终止。其所有子进程都会立刻变成**孤儿进程**,并被 `init` 进程(PID 1)收养。`init` 进程会负责等待它们终止,并清理它们的僵尸状态,避免系统资源泄露
**3. 进程状态转换与资源回收**
无论以何种方式终止,进程都会经历以下状态转换:
- **从运行状态到终止状态**:当一个进程终止后,它的所有资源(如内存页、文件描述符等)都会被释放
- **成为僵尸进程**:一个进程终止后,它的进程描述符(PCB)仍然保留在内存中,记录其退出状态,等待父进程来“收尸”。此时,该进程就进入了僵尸状态
- **父进程的责任**:为了避免子进程成为僵尸,父进程的责任是调用 `wait()``waitpid()`。一旦调用,内核就会将僵尸子进程的 PCB 彻底移除,释放其占用的 PID,完成最终的清理工作
File diff suppressed because it is too large Load Diff
+96
View File
@@ -0,0 +1,96 @@
### Linux 开机自启动方式
**系统级别的自启动方式**
这些是系统在引导过程中自动执行的脚本或服务,通常用于启动核心服务和守护进程
**1. Systemd**
Systemd 是现代 Linux 发行版(如 Ubuntu 16.04+、CentOS 7+、Debian 8+)中最主流的启动管理器。它使用 **`.service`** 单元文件来定义服务,这些文件通常存放在以下目录:
- `/etc/systemd/system/`:管理员创建或修改的服务文件
- `/usr/lib/systemd/system/`:软件包安装的服务文件,不建议直接修改
恶意程序可能会在这些目录中创建或修改 `.service` 文件,将自己伪装成一个合法的服务
**如何检查:**
- `systemctl list-unit-files --type=service`:列出所有服务的单元文件
- `systemctl status <service_name>`:检查特定服务的状态
- `systemctl cat <service_name>`:查看服务文件的具体内容,包括执行命令
**2. Init 脚本**
在较旧的 Linux 发行版中(如 CentOS 6、Ubuntu 14.04),系统使用 Init 脚本来管理启动
- **SysVinit**:脚本存放在 `/etc/init.d/` 目录,通过 `rcX.d` 目录(X代表运行级别)的软链接来控制服务的启动顺序
- **Upstart**:配置文件存放在 `/etc/init/` 目录
恶意程序可能会在 `/etc/init.d/` 中添加新脚本,或在 `/etc/rcX.d/` 中创建软链接,从而在系统启动时被执行
**如何检查:**
- `ls -l /etc/init.d/`:查看所有 Init 脚本
- `ls -l /etc/rc?.d/`:查看不同运行级别下的软链接
**用户级别的自启动方式**
这些方式通常只在特定用户登录后才会被执行,常用于启动桌面环境下的应用程序
**1. Cron 定时任务**
Cron 用于在指定的时间自动执行任务,也可以被配置为在系统启动时执行
- `/etc/crontab`:系统级别的 Cron 文件,可用于设置在系统启动时执行的脚本(使用 `@reboot` 关键字)
- `/etc/cron.d/`:存放独立的系统级 Cron 文件
- `/var/spool/cron/crontabs/<username>`:用户级别的 Cron 文件
恶意程序可能会利用 `@reboot` 字段,将自己添加到这些文件中,从而实现开机自启动
**如何检查:**
- `cat /etc/crontab`:查看系统 Cron 文件
- `ls -l /etc/cron.d/`:查看独立 Cron 文件
- `crontab -l`:查看当前用户的 Cron 任务
- `crontab -l -u <username>`:查看特定用户的 Cron 任务。
**2. 用户配置文件**
许多 Shell 和桌面环境都有自己的启动文件
- `~/.bashrc``~/.bash_profile``~/.profile`:这些文件在用户登录时会被 Shell 加载,恶意代码可以被注入其中
- `~/.config/autostart/`:桌面环境(如 GNOME、KDE)下的自启动目录。这个目录下的 `.desktop` 文件可以指定一个程序在用户登录后自动运行
**如何检查:**
- `cat ~/.bashrc``~/.bash_profile` 等:检查可疑命令
- `ls -l ~/.config/autostart/`:查看可疑的 `.desktop` 文件
**其他隐蔽的自启动方式**
除了上述常见方式,恶意程序还可能采用更隐蔽的手段。
**1. SUID/SGID 文件**
虽然不是直接的自启动方式,但拥有 SUID 或 SGID 权限的程序可以在不询问用户密码的情况下以高权限运行,这对于持久化攻击非常有用。攻击者可以利用这些程序在系统启动后被调用时,执行自己的恶意代码
**如何检查:**
- `find / -type f -perm /4000 2>/dev/null`:查找所有 SUID 文件。
- `find / -type f -perm /2000 2>/dev/null`:查找所有 SGID 文件
**2. SSH 密钥**
攻击者可以修改 `~/.ssh/authorized_keys` 文件,添加自己的公钥,从而在无需密码的情况下远程登录。虽然不是开机自启动,但它能让攻击者在系统重启后仍能获得持续访问权限
**如何检查:**
- `cat ~/.ssh/authorized_keys`:检查是否存在陌生的公钥
**3. 动态链接库**
`LD_PRELOAD` 环境变量可以让攻击者在程序启动时预先加载一个恶意动态链接库(`.so` 文件),从而劫持合法程序的函数调用,实现代码注入。如果将 `LD_PRELOAD` 变量写入全局配置文件(如 `/etc/profile`),则会对所有用户生效
**如何检查:**
- `cat /etc/profile``~/.bashrc` 等:检查 `LD_PRELOAD` 环境变量是否被设置
File diff suppressed because it is too large Load Diff
+52
View File
@@ -0,0 +1,52 @@
### Linux 有哪些系统调用
**1. 进程管理相关系统调用**
这些调用用于创建、销毁和管理进程
- `fork()`: 创建一个子进程,它是父进程的精确副本
- `execve()`: 在当前进程中加载并执行一个新程序
- `wait4()`: 暂停当前进程,等待子进程退出或停止
- `exit()`: 终止当前进程并返回一个状态码。
- `getpid()`: 获取当前进程的 ID。
**2. 文件和设备管理相关系统调用**
这些调用是应用程序与文件系统交互的核心,包括文件的创建、读写和权限管理
- `open()`: 打开或创建一个文件,返回一个文件描述符
- `read()`: 从文件描述符中读取数据
- `write()`: 向文件描述符中写入数据
- `close()`: 关闭一个文件描述符
- `lseek()`: 移动文件读写指针的位置
- `stat()`: 获取文件的状态信息,例如大小、权限和创建时间
- `unlink()`: 删除一个文件
- `ioctl()`: 用于设备特有的 I/O 操作
**3. 信息维护相关系统调用**
这些调用用于获取或设置系统信息,如时间、用户信息和系统配置
- `gettimeofday()`: 获取当前时间和时区信息
- `uname()`: 获取操作系统的名称和版本信息
- `getuid()`: 获取当前用户的有效 ID
- `getgid()`: 获取当前用户的有效组 ID
**4. 通信相关系统调用**
这些调用主要用于进程间通信(IPC)和网络通信
- `pipe()`: 创建一个匿名管道,用于父子进程间的通信
- `socket()`: 创建一个网络套接字
- `connect()`: 连接到远程主机
- `bind()`: 将套接字绑定到一个地址和端口
- `listen()`: 监听来自客户端的连接请求
- `accept()`: 接受一个客户端的连接
**5. 内存管理相关系统调用**
这些调用用于分配、释放和管理进程的内存
- `brk()`: 改变数据段的结束地址
- `mmap()`: 创建一个内存映射,用于在文件中或设备中进行内存操作
- `munmap()`: 撤销 `mmap()` 创建的内存映射
File diff suppressed because one or more lines are too long
+27
View File
@@ -0,0 +1,27 @@
### 说说 Linux 下的 Syscall
**什么是系统调用(Syscall)?**
在 Linux(及其他类 Unix 操作系统)中,**系统调用**是用户空间程序(User-space program)与内核(Kernel)之间进行交互的唯一接口
你可以将内核想象成一个操作系统的“核心”,它负责管理和控制计算机的所有硬件资源,例如 CPU、内存、硬盘和网络。而我们日常使用的所有应用程序,比如浏览器、文本编辑器或命令行工具,都运行在**用户空间**
为了确保系统的稳定和安全,用户空间的程序不能直接访问硬件。当一个程序需要做一些“特权”操作时,比如:
- 读写文件
- 创建或销毁进程
- 分配内存
- 发送网络数据包
- 获取当前时间
它就必须通过一个**系统调用**来向内核发出请求。内核会检查这个请求是否合法,如果合法,就会代表用户程序执行这个操作
**系统调用的工作流程**
一个典型的系统调用流程如下:
1. **准备参数**:用户空间的程序将要传递给内核的参数(例如,打开文件时的文件名和权限)放入指定的通用寄存器中
2. **设置系统调用号**:程序将一个唯一的**系统调用号**(Syscall Number)放入另一个特定寄存器(在 x86 架构中通常是 `EAX``RAX`)中。这个号码告诉内核,程序想要执行哪一个系统调用(比如 `open` 对应 `2``read` 对应 `3`
3. **触发软中断**:程序执行一条特殊的**中断指令**(例如 `int 0x80` 在 32 位系统上,或者 `syscall` 在 64 位系统上)。这条指令会暂停用户程序的执行,并强制 CPU 将控制权从用户空间转移到内核空间
4. **内核处理**:内核接收到中断后,会根据寄存器中的系统调用号,在系统调用表中查找并执行对应的内核函数
5. **返回结果**:内核函数执行完毕后,会将结果(成功或失败的代码)放入一个寄存器中,并执行一条返回指令,将控制权和结果返回给用户空间的程序
+20
View File
@@ -0,0 +1,20 @@
# 网安面试题(涵盖护网、红队、逆向、二进制)
上万道安全面试题已经全部为您划分好,适用于网络安全所有岗位!!!
HR:请问…………
我:叽里咕噜说啥呢,看看八股文上写了没
Summary.md 是目录噢!!)
**🙏 特别感谢名单**
在整理和完善本项目的过程中,以下朋友给予了宝贵的帮助与支持,在此表示诚挚的感谢!(排名不分先后)
- **@用户名1** —— 提供了大量安全面试题方向的补充
- **@用户名2** —— 纠正了多个问题的答案与表述
- **@用户名3** —— 贡献了真实面试题经验分享
- **@用户名4** —— 对内容结构与目录提出改进建议
如果您也愿意参与本项目,欢迎通过 **微信: XR3327026244** 投稿面试题或反馈问题,我们会在后续版本中加入您的名字
File diff suppressed because it is too large Load Diff