南京大学 操作系统 (JYY) 学习笔记:系统安全、访问控制与惊心动魄的侧信道攻击

写在前面:这是本系列的第二十六篇。

在操作系统 API 上,我们可以构建命令行工具、编译器、数据库、浏览器等丰富的应用。当越来越多用户开始共享计算机、越来越多的应用出现在操作系统中,隔离用户的权限就成为了非常重要的需求,“黑客” 也随之登上了历史的舞台。

本讲内容:我们将探讨现代操作系统中的访问控制机制,以及在“绝对安全”的系统设计之外,黑客是如何利用幽灵般的硬件侧信道漏洞(如 Meltdown)完成降维打击的。

在这里插入图片描述

计算机系统安全简介:从“裸奔”到现代防护

PC“裸奔”时代

8086, Real-Mode (实模式)

  • 16-bit CS, 16-bit IP, 物理地址 PC = (CS << 4) + IP (只有 1 MB 的寻址能力)。
  • 固件代码 (Firmware code) 直接映射到地址空间中。
    • int $x \rightarrow ((void(*)())(x \times 4))();
    • 这就是 16-bit 时代简陋的“系统调用”。

完全没有任何安全可言

  • 任何程序都可以访问任何硬件
  • 这造就了早期病毒的狂欢:病毒可以随意复制自己,然后感染更多的计算机(“加壳”技术)。
  • 例子:著名的 CIH 病毒,甚至能直接破坏主板的 BIOS 硬件芯片!

安全不裸奔的三大基石 (CIA Triad)

现代信息安全建立在三个核心支柱之上:

1. Confidentiality (机密性)

  • 不想给别人看的,别人就看不到。
  • (比如快速格式化后,如何保证数据不被恢复?)

2. Integrity (完整性)

  • 不想让别人改的,别人就改不了。
  • (你们的师兄:“如何通过入侵老师邮箱拿到期末考卷和修改成绩…”)

3. Availability (可用性)

  • 属于我的,别人不能让我用不了。
  • “Denial-of-service” (DoS) 拒绝服务攻击:
    • 发送海量请求,堵死服务器带宽,导致正常用户无法服务。
    • 使用 fork 恶意拉起无数个线程,耗尽系统内存和 PID 空间,使得正常功能不能运行。
    • 经典 Fork bomb (Bash 炸弹): :(){ :|: & };:
    • Algorithmic complexity attack; 例子:Hash table 碰撞攻击
    • Distributed DoS (DDoS) 分布式拒绝服务。

安全是个巨大的主题

软件 + 硬件协同设计(参阅顶会 IEEE S&P (Oakland) 2025):

  • 分页机制和进程隔离
  • 系统调用和访问控制
  • 鉴权与授权
  • 加密
  • 审计与日志
  • 机密计算 (TEE)……

今天我们只问一个问题:
操作系统的设计者,如何在基础“功能”之上,优雅地实现 Confidentiality, Integrity, Availability?


访问控制 (Access Control)

访问控制的本质

进程 + 虚拟内存已经实现了隔离。

  • 进程只能以 ELF 规定的权限访问自己的虚拟地址空间。
  • 系统调用是唯一访问操作系统对象的途径(假设内核没有被漏洞攻破)。
  • 操作系统还有更高维度的虚拟化机制:虚拟机、容器 (cgroups, namespaces…)。

访问控制的核心:限制程序对操作系统对象的访问。

  • 拒绝越权访问 $ \rightarrow $ 保证机密性 (Confidentiality)
  • 拒绝越权修改 $ \rightarrow $ 保证完整性 (Integrity)
  • (再加上公平的资源调度机制 $ \rightarrow $ 保证可用性 Availability)

访问控制原理:一张表

从上帝视角看,系统只需要维护一张巨大的权限控制表:“谁能怎么访问什么”。

进程对象访问权限
1/etc/passwdread
1/etc/passwdwrite
4132/etc/passwdwrite×
4132/tmp/hello.txtwrite
  • 越权访问,操作系统直接返回 EACCESS (Permission denied)。
  • 缺点: 如果在内核里真维护这么一张表,它将变得极其庞大,而且极难维护。我们需要一个更简单、更低成本的机制。

UNIX 的绝妙抽象:用整数表示身份

为了避免维护一张庞大的字符串表,UNIX 引入了极简的 uid, gid, mode 机制:

  • uid = 0 $ \rightarrow $ root,其他都是“普通用户”。
    • root 是真神,可以访问所有对象,也可以使用 setuid 任意切换身份。
    • 子进程完美继承父进程的 uid
  • gid “完全自由”(虽然一般 0root 组)。
    • 一个用户可以属于多个不同的组。
  • mode: 即 r, w, x (读、写、执行) 权限位。
    • 例子:owner 只写不可读,audit 组可以读的日志文件。

操作系统底层其实完全看不到用户名!
它只认识冷冰冰的数字 uid。所有的权限隔离,纯粹通过 setuid, setgid, chmod 系统调用实现。

/etc/passwd:每行一个用户

username:password:uid:gid:comment:home:shell

  • 现代系统通常使用 /etc/shadow 文件存储密码的 hash 值(极大地提高了安全性)。
  • chsh, passwd 命令本质上只是具有特权的小程序,直接去修改了这些文本文件而已。

UID:没有那么简单 (Spicy 🌶️)

没有一个软件系统 (Abstraction) 能逃脱成为“💩山”的宿命。

随着业务的复杂化,UNIX 被迫把纯粹的 uid 拆分成了极其变态的体系:

  • Real uid (ruid): 真实的用户 ID。
  • Saved uid (suid): 为了实现“临时提权后恢复权限”而设计的备份 ID。
  • Effective uid (euid): 这是操作系统实际进行访问控制检查时真正看的 UID!
  • Filesystem uid (fsuid): (Since Linux 1.2,现在几乎没人用了)。
  • chmod +s (Set-UID 标志,比如 ls -l /bin/passwd 里的 s,允许普通用户在执行该程序时瞬间化身为文件所有者的 euid)。

深入阅读:Setuid demystified

回到访问控制:现代扩展 (Spicy 🌶️)

uid, gid, mode 已经无法满足现代云原生安全的需求了,于是诞生了更多花活:

  • Access Control List (ACL):
    基于文件系统的扩展属性 xattr 实现,支持为任意数量的特定用户和组设置极其精细的权限。
  • SELinux / AppArmor:
    强制访问控制 (MAC)。可以试试 sudo apparmor_status | ag --gpt -q Explain
  • Capabilities:
    把原本无所不能的 root 权限大卸八块。你可以给一个普通程序赋予“只允许发 Ping 包”的特权,而不给它任何其他 root 能力。
    capsh --drop=cap_net_raw -- -c 'ping 127.0.0.1' (注意这是 fail on execve; 可用 getcap 查看 capabilities)。

意料之外,之外,之外…… (Spicy 🌶️)

攻破一个进程

Undefined behavior (未定义行为) 不是在和大家开玩笑的!

  • 一个看似不起眼的缓冲区溢出 (Buffer Overflow) 或者悬空指针。
  • 内存错误 (Memory error) 贡献了巨量的现代内核提权漏洞。
  • 进阶教程:Tutorial: 64-bit Overflow

防御一个进程:减少攻击面

黑客很强,但防守方也在不断进化,疯狂拉高攻击的门槛:

  • 降权: 永远以 www-root 等最低权限运行对外服务。
  • ASLR (地址空间布局随机化): 每次启动,mmap 把栈、堆、库映射到完全随机的地址,让黑客找不到跳转的目标。
  • Canary (金丝雀/栈保护): 在函数返回地址前埋一颗“地雷”,一旦发生溢出覆盖,地雷立刻引爆,程序自杀。
  • NX-bit (不可执行位): 栈区和数据区被标记为永远不可执行代码。
  • CFI (控制流完整性): 硬件级别的跳转地址合法性校验 (如 Intel 的 endbr64)。

访问控制:没能完全解决问题 (侧信道攻击)

“单次”看起来绝对合法的访问,如果在时间、功耗上存在差异,也可能被提炼出极其致命的机密信息。

经典的 Tenex 密码验证系统漏洞:

int check_password(__user char *given_pass) {
    ...
    for (i = 0; i <= strlen(correct_pass); i++)
        if (correct_pass[i] != given_pass[i]) {
            sleep(3);
            return EACCESS; // access denied
        }
    return 0;
}

极客黑魔法:
上面的代码看起来没问题?一旦第一个字符错了就立刻返回错误。
但黑客可以故意把 given_pass 字符串放在一个内存分页边界 (Page Boundary) 上!
如果第一个字符猜对了,内核会去读第二个字符,此时会触发一个极度耗时的缺页中断 (Page Fault)。
黑客通过精确测量程序返回 EACCESS微秒级时间差,就能在 $ O(N) $ 的常数时间内,一个字符一个字符地把正确密码完全“试”出来!

Meltdown:硬件芯片底层的崩塌

软件没问题,硬件也可以有漏洞!这就大名鼎鼎的“熔断”漏洞 (Meltdown)。

raise_exception();  // 第一步:故意触发一个异常,比如非法读取高权限的内核内存
uint8_t volatile x = probe_array[data * 4096]; // 第二步:利用非法拿到的 data 去访问数组

漏洞原理拆解 (Timing side channel):

  • 推测执行 (Speculative Execution): 现代处理器为了提高性能,在等待异常处理完成时,会提前“偷跑”后续的指令。
  • 缓存侧信道攻击: 虽然 CPU 最后发现你越权了,撤销了这些推测执行的寄存器结果,但它没有撤销 CPU 高速缓存 (L1/L2 Cache) 里留下的痕迹!
  • 用户虽然读不了内核数据,但可以通过遍历探测 probe_array 的访问速度,瞬间识别出哪一块内存被缓存了,从而反向逆推出那个被非法读取的 data 是什么!
  • 天道好轮回: 现代硬件的精妙性能设计,反而成了安全的地狱。
  • 目前的惨痛补丁: KPTI (Page Table Isolation)。 直接改写了教科书,强行在内核态和用户态切换时清空页表,代价是巨大的性能损耗。

你甚至看不到你的对手在哪

硬件侧信道攻击的脑洞是没有极限的:

1. 电磁辐射窃听 (Electromagnetic eavesdropping)

2. 物理声学窃听

  • 只要录下你敲击键盘的声音(每个按键在键盘不同位置产生的高频声纹有微小差异),经过 AI 降噪分析,就能在隔壁房间把你输入的密码完整记录下来。
  • Physical key extraction attacks on PCs

总结

Take-away messages:

在计算机系统设计的初期,一切为了功能,真的没有丝毫安全可言。但随着商业需求的暴涨和黑客的步步紧逼,今天我们已经建立起了极其庞大、成熟的安全隔离机制,从软件层的 UID/进程隔离,到硬件层的 ASLR/NX 保护,层层设卡。

然而,“攻与防”的艺术永远没有尽头。对于极客而言,用出其不意的方法(例如时间差、电磁辐射、CPU 分支预测漏洞)去攻破这些看似坚不可摧的系统,既是最高级的心智挑战,也是推动计算机底层架构不断向前进化的核心动力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值