第一章 中央处理器CPU

1.1.1 逻辑门

1.1.2 加法器

1.1.3 算术逻辑单元ALU

  • 既可以算术运算,也可以逻辑运算
  • 本质都是通过输入控制的开关
  • 乘法转换成加法
  • 除法转换成减法
  • 减法转换成加法

1.2.1 指令集

  • 指令机器码 CPU支持的所有指令的编码(比特流)
  • CPU的指令集 所有的指令
  • RISC 一条指令只完成一个基本操作的精简指令集,指令长度基本固定
  • CISC 一个指令可以完成一个复杂功能的复杂指令集,指令长度基本不固定

1.2.2 寄存器

  • 原因 存储在内存不安全,读写效率慢
  • 寄存器 CPU内部保存数据的存储电路
  • 有指令操作寄存器

1.2.3 汇编语言

  • 汇编语言助记符来编程的语言

1.2.4 高级语言

  • 高级语言 接近人类的自然语言来编程的语言
  • 编译器
  • 编译 高级语言转换成机器指令

1.2.5 指令执行过程

  1. 读取指令
  2. 指令译码
  3. 指令执行
  4. 数据会写

1.3.1 指令流水集

  • 改进执行指令的流程,提升性能

1.3.2 流水线的级数

  • 将指令执行过程拆分更细
  • 进一步减少CPU电路资源的浪费
  • 但是级数过多会增加额外的电路设备,产生额外的功耗,造成额外的时间开销

1.3.3 流水线里的冒险

  • 原因
    • 访问同一电路(内存、输出)导致流水线停顿
    • 指令所需数据来自前一指令
  • 结构冒险 流水线中出现硬件资源竞争
  • 数据冒险 流水线中后面的指令需要等待前面指令完成数据的读写
  • 控制冒险 流水线需要根据前面的指令的执行结果来决定下一步去哪里执行

1.4 缓存

  • 缓存 保存从内存的数据和指令
  • 缓存行 管理内存的单元
  • 取模映射 内存中的数据只能存在缓存中的固定位置,方便存储和访问
  • 二路组相联 每个cache分成两组,即两个缓存行
    • 降低冲突——允许多个具有相同索引但不同标记的数据同时被缓存
    • 提高命中率——每个组有两个缓存行,它可以存储更多的数据
  • 指令缓存 存储指令
  • 数据缓存 存储数据
  • 三级缓存技术
    • 一级缓存
    • 二级缓存
    • 三级缓存(CPU多核共用

1.5 多核缓存不一致问题

  • 原子操作 不可切分的动作
  • 片内总线 多核之间进行信息沟通
  • 缓存一致性协议(缓存行的四种状态)
    • 已修改(Modified, M)
    • 独占(Exclusive, E)
    • 共享(Shared, S)
    • 无效(Invalid, I)

  • 规定一个内存被多个核缓存,不允许多个核同时修改缓存❎

1.6 指令乱序执行

  • 数据冒险与流水线停顿
  • 数据冒险 流水线中后面的指令需要等待前面指令完成数据的读写
  • 乱序执行 先执行不需要依赖前面数据的指令,打乱指令执行的顺序
  • 保留站 一个缓冲区登记指令是否有数据依赖、具体依赖什么数据、需要用到的执行部件有哪些、当前是否繁忙、以及需要读写的寄存器

1.7 控制冒险与跳转指令

  • 静态预测 不管前面结果,直接假定分支不会跳转继续把后续的指令载入流水线处理
  • 分支预测 记录最近跳转的次数,根据最近多次跳转的结果再来预测

1.8 一条指令同时处理多个数据

  • MMO〜MM7 64位寄存器,同时存储两个32位的整数和8个8位的整数(借用用浮点数运算单元FPU的寄存器)
  • XMMO〜XMM7 128位寄存器
  • 单指令多数据流SIMD 在一条指令中同时处理多个数据的技术

1.9 一个核同时执行两个线程

  • 资源闲置 比如整数运算时浮点数运算电路会闲置
  • 超线程 单线程改多线程,效率不能翻倍但有提升

1.10 管理内存

  • 分段式存储管理 寄存器16位,最大64KB的段
  • 访问内存 段地址+段内偏移地址
  • OS划分时间片
  • 虚拟内存
  • 分页交换 把暂时不用的页面放到硬盘,使用页错误中断换回页面

1.11 地址翻译

  • 内存管理单元MMU
  • 页目录索引
  • 页表索引
  • 页内偏移
  • 地址翻译 从CR3寄存器中取出页目录地址,根据页目录索引找到页表,再根据页表索引找到物理内存页面,最后根据页内偏移,完成寻址
  • 地址翻译缓存 把翻译的虚拟地址和物理地址的映射关系放入缓存(局部性原理)
  • 地址转换后援缓冲器(快表)TLB

1.12 GPU

  • 多计算电路,少逻辑控制电路
  • SIMT并行计算 (批量计算算法固定)

  • 多ALU and 多执行上下文 (充分利用计算资源,不让ALU闲置)

第二章 存储设备

2.1 缓存为什么快

  • 内存 动态随机存储器DRAM(1个DRAM单元——一个MOS晶体管和一个电容,可以存储1比特的信息)
  • 缓存 静态随机存储器SRAM
  • 缓存因为为没有依靠电容充放电,全都是晶体管的导通与断开,比起内存的DRAM速度要快得多
  • 缺点 成本高、占用空间大

2.2 内存条

  • 存储芯片 黑乎乎的东西8~16个
  • PCB电路板
  • 金手指 连接主板插槽的接触点
  • RAM 随机存储器
  • 动态数据刷新 DRAM“漏电”电容的电荷消失,需要周期性充电刷新来维持数据的稳定
  • 内存控制器
  • 指定芯片、分片、格子的行地址和列地址来访问比特位
  • 读写单元 一字节8个比特位

2.3 多个CPU共同访问内存

  • 非一致性内存访问NUMA(Non Uniform Memory Access)
    • 16核拆分成两个CPU,组成两个NUMA节点Node
    • 每个节点直接连接一部分内存
    • 内连接(inter-connect)通道 两个CPU之间的通道
    • 本地访问
    • 远程访问
    • 远程访问效率高于页面置换,内存不够时优先远程访问

2.4 硬盘

  • 金属磁粒 机械硬盘盘面存储数据的东西

  • 读磁头 通过电磁检测磁粒的极性分辨0和1
  • 写磁头 通过磁场改变单元格中金属磁粒的极性,设定成0或1

2.5 磁盘管理

  • 块block 读写的基本单位,把连续的扇区当成一个整体
  • 块位图 记录哪些块是空闲的,哪些块是被占用的(记录在第一个块)
  • inode 记录每一个文件的大小、位置、权限、时间等,每一个都是128字节,并且每一个都有一个专属号码在inode表

  • 目录
  • 根目录
  • 描述符 记录inode表、块位图、inode位图的位置信息
  • 超级块 记录硬盘总共用了多少块,还剩多少块

  • 启动扇区DBR 装载引导程序
  • 主引导扇区MBR 记录所有的分区信息,位于硬盘的第一个扇区

第三章 数据的输入与输出

3.1 总线

  • 总线 包含传输数据的数据总线、传输地址的地址总线和进行控制管理的控制总线
  • 总线控制器 统一管理总线
  • 北桥芯片 集成内存控制器、总线控制器、图像控制器(访问内存和显卡)高速设备
  • 南桥芯片 集成各种IO外部设备的控制器(低速设备)
  • 一条总线 → 多个层级总线组成的总线系统
  • NOW CPU集成内存控制器和图像控制器 重新变回一条总线

3.2 中断

  • eflags寄存器 存储当前CPU是否可以被中断的值
  • 不可屏蔽的中断NMI
  • 可编程中断控制器PIC
    • 中断向量
    • 中断描述符表IDT 记录处理中断对应的函数地址
    • idtr寄存器 指向中断描述符表IDT的内存地址
    • 异常处理 也是这个表,但异常需要立即处理
    • 异常是同步的,中断是异步的
  • 高级可编程中断控制器APIC
    • IO APIC
    • Local APIC
    • 处理器间中断Inter-Processor Interrupt IPI
  • 中断亲和性
  • 亲和寄存器

3.3 计算机启动

  • 自检工作 所有寄存器全部重置,如有错误记录到EAX寄存器
  • 引导处理器BSP
  • 主引导记录MBR 512字节,检测最后两个合法字节是0x55和0xAA

3.4 数据搬运

  • 可编程输入输出模式PIO 执行in和out两条指令对外部设备读写数据
  • 直接存储器访问DMA 设置寄存器传输哪里的数据,从哪里到哪里,长度是多少
  • DMA控制器DMAC

3.5 零拷贝技术

  • 零拷贝技术 把从硬盘读取的数据缓冲区地址和长度给网络socket描述符

3.6 网卡

  • 集线器Hub 收到的信号做一个增强处理后发送给所有端口
  • CSMA/CD 载波侦听多路访问/冲突检测
  • 以太网帧长度不能低于64字节,这样就算在最远两端发生的碰撞冲突都能及时传递回去被检测到

  • ARP 地址解析协议 发送目的地址是FF:FF:FE:FF:FF:FF的广播,匹配的发送回去
  • ARP欺骗
  • 网卡的混杂模式 把总线的全部数据帧抓取交给CPU处理
  • 交换机 记录对应的MAC地址和端口号,精准发送
  • 全双工通信
  • 冲突域
  • 数据帧校验 对帧检验序列FCS进行循环冗余码校验CRC
  • RX FIFO队列 网卡内部的接收队列缓冲区
  • DMA控制器 传输网卡的数据
  • 硬中断 硬中断需要快速完成
  • 软中断 硬中断无法快速完成的调用软中断处理函数进行处理
  • NAPI 数据包过多采用中断+轮询
    • 第一部分 硬中断通知,关中断,但不处理数据包
    • 第二部分 软中断轮询处理,不需要关中断
  • Netfilter Linux内核的一个子系统,允许实现各种与网络相关的操作
  • 协议栈

3.7 直接收发数据包

  • 每次中断都要保存上下文,从用户态切换到内核态
  • 线程亲和性 网络监控软件的工作线程独占CPU核心,解决缓存失效问题
  • DPDK
    • 通过操作系统的用户态模式驱动UIO,在用户态通过轮询的方式读取网卡的数据包
    • 直接在用户态读取,不用把数据包在内核态空间和用户态空间搬来搬去
    • 读取后直接分析,不用走系统协议栈和netfilter

  • 大页内存技术 支持2MB和1GB管理内存页面
  • Interrupt DPDK
    • 没有数据包处理时就进入睡眠,改为中断通知
    • 共享CPU核,不独占CPU
    • DPDK线程有更高的调度优先级
    • 数据包多后变轮询模式,灵活切换

第四章 操作系统

4.1 控制程序

  • 多道程序处理
  • 时间分片
  • 时钟中断
  • 任务状态
    • 创建
    • 就绪
    • 执行
    • 阻塞
    • 终止

  • 优先级
  • 抢占 高优先级程序出现时,低优先级的程序未完成被剥夺执行机会
  • 多核时代 → 操作系统

4.2 进程

  • 1号进程init
  • PID 进程的身份证
  • 访问越界
  • 内核地址空间 操作系统内核运行的空间
  • 进程调度
  • 一个进程里可以同时存在多个执行流,也就是多个线程,每一个线程都有自己的执行上下文和堆栈,互不影响
  • 线程 操作系统调度执行的单位
  • 并发执行 运行多个不同的线程

4.3 线程

  • 完全公平调度算法CFS 红黑树管理线程
  • 只要进程的其中一个线程挂掉,所有线程都会被结束掉
  • 权重 权重越高的线程越优先被运行
  • 等待队列

4.4 系统调用

  • 内核地址空间 操作系统内核所在的内存区域
  • 用户地址空间 应用程序访问的内存区域
  • 系统调用表 sys_call_table

  • 系统调用 操作系统将管理文件、内存、进程、线程、网络、硬件等的在内核地址空间操作封装成函数接口方便应用程序调用
  • 内核栈 线程有两个栈,一个在用户态地址空间,一个在内核态地址空间,内核栈会小很多
  • syscall sysret 一对的

4.5 异常处理

  • 中断描述符IDT 记录所有中断和异常需要处理的地方
  • 异常 CPU在执行线程的代码指令时出现了错误
  • idtr寄存器记录中断描述符IDT
  • 信号投递
  • 触发异常时,CPU自动保存的现场(返回地址和一些其他关键寄存器的值)
  • iret(interrept return) 专门用于被中断或异常打断的线程处理完毕后返回用户态地址空间

4.6 信号处理

  • 可靠信号和不可靠信号
  • 进程的描述符task_struct
  • 进程不能直接调用这些信号处理函数

  • 调用sigprocmask函数屏蔽信号
  • SIGKILL和SIGSTOP无法屏蔽
  • 一个进程实际就是一个线程组
  • task struct中原来的信号等待队列只存放各个线程自己的信号,另外再单独设置一个队列来存放进程的信号,让所有的线程共享
  • 给线程投递信号(group=0 )
  • 给进程投递信号(group=1 )
  • 处理信号处理函数的表格是整个进程共享的

4.7 锁

  • 原子操作
  • 自旋锁 获取锁的时候线程会一直循环检查状态

  • 互斥锁 获取锁的时候线程进入锁的等待队列,交出CPU执行权限进入睡眠,等待唤醒
  • 条件变量 等待条件变量的线程平时阻塞着,别的线程发现条件满足之后,就将条件变量激活
  • 信号量 升级版互斥锁,有计数器指定最多允许多少个线程同时获得它

4.8 Linux的权限管理

  • 文件打开的过程

  • 常规DAC检查 genenric_permission函数
  • 文件的归属用户id保存在文件索引inode
  • 进程的用户id保存在进程的task_struct(task_struct->cred的fsuid)
  • Linux操作系统为所有文件针对所属的用户、所属的用户组和其他用户分别设置了访问权限
  • 读(Read )、写(Write )、可执行(Execute )三种权限
  • UGO权限管理方式 权限信息和文件的归属信息记录在索引信息inode
  • ACL访问控制列表 单独记录一些细粒度的权限信息(校验完进程所属用户和文件所属用户后,就会进入ACL的检查)
  • Cgroup检查 是否有权访问对应的设备

4.9 Docker

  • 轻量级的虚拟容器 只提供一个运行环境,不用运行一个操作系统,所有容器的系统内核与宿主机共用
  • chroot和pivot_root 可以将进程看到的根目录修改为一个新位置(“伪造一个文件系统欺骗容器的进程”)
  • 操作系统镜像文件和进程依赖的目录和文件通过联合挂载的方式,挂载到容器进程的根目录下,变成容器的rootfs,和真实系统目录一模一样
  • 命名空间namespace 划定一个个的命名空间,把进程划分到这些命名空间中,每个命名空间都是独立存在的,命名空间里的进程都无法看到空间之外的进程、用户、网络等信息
  • Cgroup 划定一个个分组,然后限制每个分组能够使用的资源,比如内存的上限值、CPU的使用率、硬盘空间总量等(系统内核会自动检查和限制这些分组中的进程资源使用量)

第五章 系统编程

5.1 进程

  • Linux 一切皆文件
  • fork函数 创建进程
    • 一次调用会返回两次
    • 父进程返回进程号
    • 子进程返回0
    • 子进程和父进程共享内存空间

  • 写时拷贝(COW)机制 允许只读,写入会触发异常分配新页面
  • fork创建子进程只拷贝当前线程,不拷贝其他线程

5.2 线程的栈

  • 后进先出LIFO
  • push和pop指令 压入数据和弹出数据
  • call指令 调用函数(把call指令后面的那条指令地址保存到栈里面,等调用完函数后才能回来继续往后执行)
  • ret指令 函数执行完后用来返回到调用它的地方的指令,CPU在执行ret指令的时候,就会把之前保存在这里的地址取出来,跳转过去
  • 使用寄存器传参比使用线程栈传参快很多
  • ulimit -a 查看线程栈的大小
  • 自动增长 缺页异常处理时发现是线程栈会分配新内存页面
  • 但是如果超出上限的话会杀死进程
  • 内核栈 页面小(4KB~8KB)小心使用递归调用
  • 栈溢出攻击 将栈里保存的返回地址覆盖为恶意代码的地址

5.3 进程通信

  • 信号 Linux的一种软中断通信机制,总共64种信号,只能通知不能传输数据
  • kill -l 查看所有信号
  • socket套接字
  • 127.0.0.1 本地回环地址,数据在协议栈转发,可以在虚拟的回环网卡lo抓取数字

  • 匿名管道 内核的一段缓冲区,提供读写两个端口,单向

  • 消息队列 内核的一个消息链表,可以指定类型
  • 命名管道 有名字就不限制进程通道,只要使用名字都可以打开管道通信

  • 共享内存

5.4 IO多路复用

  • select函数 遍历所有文件描述符,挂入相关联设备的等待队列后进入阻塞,如果设备有消息提供回调函数通知Web服务进程
  • select使用位图数组,最多同时处理1024个文件描述符
  • poll模型 使用链表存储,可容纳更多文件描述符
  • epoll模型
    • 采用红黑树管理监听的文件描述符方便查找
    • 采用双向链表管理队列,不需要遍历所有的文件描述符

5.5 读写文件

  • 数据结构 缓存文件数据块信息存到内存

  • fsync函数 进行同步将缓存写入硬盘
  • 内存映射文件 将文件的数据缓存页映射到进程的用户台地址空间
  • 把整个文件或者文件的一部分直接映射到应用程序的地址空间

5.6 协程

  • 线程可以在执行函数遇到阻塞后,保存执行的上下文,转而执行别处的代码。待阻塞的请求完成后,再回去继续执行

  • 协程 在一个线程中,可以抽象出多个执行流协程,由线程来统一调度管理
  • 操作系统可以通过时钟中断系统调用(抢占式调度)进入内核来剥夺线程的执行权
  • Golang 设计为支持协程,封装好系统调用方便协程调度器管理

5.7 调试器GDB

  • ptrace函数
1
2
3
4
5
6
long ptrace(
enum __ptrace_requeset request, // 第一个参数是一个枚举型的变量,表示要执行的操作
pid_t pid,
void *addr,
void *data
);
  • break 设置断点
    • 把被调试进程中那个位置的指令修改为0xCC
    • CPU执行这条特殊的指令会陷入内核态,然后取出中断描述符表IDT的3号表项中的处理函数来执行
    • 系统内核拿到CPU的执行权,会发送一个SIGTRAP信号给被调试的进程
    • GDB截获信号,检查是不是设置的断点,显示断点触发
    • 在没有下一步指示之前,被调试的进程都不会进入就绪队列被调度执行
  • 单步执行
    • eflags标志寄存器 包含了程序运行的一些状态和一些工作模式的设定
    • TF标记 告诉CPU进入单步执行模式
  • 内存断点
    • watch 监视被调试进程中某个内存地址的数据变化
    • 调试寄存器 DR0到DR7总共8个,在DRO〜DR3中设置要监控的内存地址,在DR7中设置要监控的模式(读或写)

5.8 可执行文件ELF

  • do_execve_commo函数 启动可执行文件的函数
  • prepare_binprm函数 读取文件头部的128字节数据,放入内存缓冲区
  • search_binary_handler函数 在可执行文件处理节点的链表中寻找处理可执行文件的模块
  • load_script函数 脚本类型程序的加载函数
  • 程序头表 结构数组,每一个结构都记录一个段(segment)的信息
  • 进程地址空间中的一块区域,由一个节(section )或者多个节构成
  • 存放ELF文件的数据,比如静态数据、代码指令、调试信息

  • 加载过程
    • load_elf_binary函数
    • 引用动态链接库需要解释器加载
    • 静态编译则不需要
    • 检查可执行栈
    • 加上随机偏移 难以推算数据和函数的内存地址

第六章 计算机攻击与安全

6.1 TCP序列号

  • 中间人攻击 监听网络通信拿到通信的序列号和确认号,伪造进行通信

  • 初始序列号INS 计数器,每4ms加1(不能直接发送,会被别人算出新的ISN)
  • ISN = M + F(localhost, localport, remotehost, remoteport)
    • M 计数器
    • F MD5算法
    • F参数 通信双方的IP和端口
  • ISN = M + F(localhost, localport, remotehost, remoteport, secretkey)
  • 同名计数器 因为CPU为8核防止多个线程之间竞争
  • TCP定时器
  • TCP计数器 DelayedACKLost

6.2 TCP SYN Flood

  • SYN洪水攻击 收到SYN数据包后,需要准备一个数据块来存储客户端的信息,发送大量SYN数据包需要分配大量数据块直到空间耗尽
  • SYN Cookie 第二次发给客户端的序列号是一个哈希值,第三次握手时计算哈希值+1=ACK 为正常数据包,错误数据包直接丢弃

6.3 HHTPS

  • 对称加密
  • 非对称加密
  • 非对称与对称加密结合 使用非对称加密算法传输加密内容的密钥
  • 密钥计算

  • 中间人攻击 冒充服务器和客户端通信,冒充客户端和服务器通信
  • 数字证书
  • 根证书 验证最终的签发者是否在根证书列表中

6.4 漏洞攻击

  • 栈金丝雀Stack Canary 抵御栈溢出攻击
  • 虚函数攻击 覆盖虚函数表指针,指向一个假的虚函数表,表格写入恶意代码地址
  • 虚函数表指针一般都是在对象的头部(最前面8个字节)
  • KPTI内核页表隔离 线程运行在用户态和内核态时使用不同的页表
  • 侧信道 通过判断内存的访问速度来获知是否有被缓存

6.5 SGX

  • 安全访问级别 Ring0-Ring3

  • Enclave安全空间
    • 创建 通过执行ECREATE指令创建一个安全空间
    • 初始化 通过执行EINIT指令对安全空间进行初始化
    • 进入&退出 通过执行EENTER/EEXIT指令进入和退出安全空间
    • 中断&异常 通过执行AEX指令退出,将在安全空间执行的上下文保存起来
    • 系统调用 通过执行AEX指令退出,执行完系统调用再进来
    • 函数调用 安全空间和外部可以互相调用函数,普通空间调用安全空间函数叫ECALL,安全空间调用外部空间函数叫OCALL
    • 销毁 通过执行EREMOVE指令销毁一个安全空间
  • 内存加密 内存加密引擎MEE(memory encryption engine)电路,对安全空间的数据进行透明的加解密,数据写入内存时加密,读取CPU内部时解密

6.6 挖矿病毒

  • top命令 常用的实时系统监控工具,提供了一个动态的、交互式的实时视图,显示系统的整体性能信息以及正在运行的进程的相关信息
  • ps命令 显示当前进程的状态
  • netstat命令 打印所有的网络连接信息
  • unhide 网络取证工具,能够发现那些借助 rootkit、LKM 及其它技术隐藏的进程和TCP/UDP 端口
  • Redis持久化存储
1
2
3
CONFIG SET dir /root/.ssh # 指定保存地址
CONFIG SET dbfilename authorized_keys # 指定保存的文件名
SAVE # 将数据保存到文件
  • 原因 Redis默认没有密码,可以使用命令行或直接修改redis.conf文件设置密码

6.7 整数+1引发的内核攻击

  • IDT表项的结构图

  • 无符号整数与有符号整数的切换
  1. 精心设计一个config值,从应用层传入内核空间的perf_swevent_init函数
  2. 利用内核漏洞,把一个64位无符号数赋值给一个int型变量,导致变量溢出为一个负数
  3. 禾ij用溢出的event_id越界访问perf_swevent_enabled,指向IDT的表项、将第四项中断处理函数的高32位进行+1
  4. 修改后的中断处理函数指向了用户空间,提前在此安排恶意代码。
  5. 应用层执行int 4汇编指令,触发4号中断,线程将进入内核空间,以内检权限执行提前安排的恶意代码

6.8 从虚拟机逃脱

  • 虚拟化技术
  • 硬件辅助虚拟化 操作系统和程序的指令都是在真实的CPU上执行的,不再用软件来解释模拟
  • 虚拟机监控程序HyperVisor
  • 虚拟机逃逸技术 虚拟机会和外面的真实计算机通信,抓住通信过程中的漏洞,把指令代码掺杂在通信数据中可以逃逸出去

  • 漏洞编号CVE Common Vulnerabilities and Exposures 显示年份和具体漏洞编号