06 - mmap and the process address space

06 - mmap 和 进程的地址空间

入侵进程的地址空间

1. 回顾

关于进程的第二课...

1.1. 新闻

...深度强化学习

Reinforce learning?

1.2. git init...

README.md / TODO.md..

git repo 的 初始化版本

通过 commit 进行状态的转移

everything is state machine...

1.3. 我们如何应对?

我们还得学 操作系统

  • 操作系统依然很重要

  • 编程语言是“可信可验证”的桥梁

    • Markdown,js,Python,...

  • 操作系统是运行支撑

    • 虚拟环境、文件系统快照、网络...

程序必须要到一个真实的环境...大语言模型终究会产生幻觉......

agent...

今天我们可以使用 docker...使用隔离机制...本地文件不受影响...

1.4. 状态机的生命周期管理 API

fork,execve 和 _exit

  • 操作系统视角:状态机的复制、重置和删除

int pid = fork();
if (pid == -1) { // 错误
    perror("fork"); goto fail;
} else if (pid == 0) { // 子进程
    execve(...);
    perror("execve"); exit(EXIT_FAILURE);
} else { // 父进程
    ...
    int status;
    waitpid(pid, &status, 0); // testkit.c 中有
}

2. 进程的初始状态

我们可以创建进程...然后会进行执行...

2.1. 进程 execve 后的初始状态

更深层次的理解...

寄存器

  • 直接进行打印

内存

  • 有点难办

    • 可以把任何整数 cast 成 pointer

      • 32 位 整数 - 32 位 指针

    • 用工具,看真实系统

暂时忽略操作系统管理的状态:pid,打开的文件...

进程空间里面的某些空间(绝大部分)是不能访问的...

那么 到底哪些可以访问???

  • 所有一切都是有答案的!

  • 没有黑魔法

向 AI 真诚发问...就会得到进步...

2.2. 结论

进程 execve 后的初始状态

  • ABI 中规定的 initial state (System V ABI)

    • Section 3.4: “Process Initialization”

    • 只规定了部分寄存器和栈 (argv 和 envp 中的字符串保存在栈中)

  • Binary 中指定的 PT_LOAD 段

    • 内存是分成 “一段一段” 的

    • 每一段有访问权限 (rwx)

2.3. 进程的地址空间

配合 gdb 调试器,我们甚至可以 “随时查看程序的地址空间”。这一切代码都是人工智能生成的!因为 AI 知晓几乎所有的细节,它是帮助我们理解复杂细节的利器。

root@LAPTOP-GT06V0GS:/mnt/d/CSLab/osCourse/lec6/address-space# ./alloc
mmap: 7fd2c4e88000
Read get: 2
Read get: 0
Read get: 3
root@LAPTOP-GT06V0GS:/mnt/d/CSLab/osCourse/lec6/address-space# ./mmap-demo
This is allocated memory at 0x7fe150f8a000
Mapped executable at 0x7fe150ec0000, size: 823704 bytes
First 16 bytes of executable: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
root@LAPTOP-GT06V0GS:/mnt/d/CSLab/osCourse/lec6/address-space# ./simple

3. 进程的地址空间管理

3.1. 往后想一想

进程的初始状态

  • 只有ELF 文件里声明的内存和一些操作系统分配的内存

    • 任何其他指针的访问都是非法的

    • 如果我们从输入读一个 size,然后 malloc(size)

      • 内存从拿来呢?

一定有一个系统调用可以改变进程的地址空间

如果让你设计操作系统的 API

  • 你会怎么设计?

3.2. Memory Map 系统调用

在状态机状态上增加/删除/修改一段可访问的内存

  • MAP_ANONYMOUS: 匿名 (申请) 内存

  • fd: 把文件 “搬到” 进程地址空间中 (例子:加载器)

  • 更多的行为请参考手册 (复杂性暴增)

// 映射
void *mmap(void *addr, size_t length, int prot, int flags,
           int fd, off_t offset);
int munmap(void *addr, size_t length);

// 修改映射权限
int mprotect(void *addr, size_t length, int prot);
  • mmap 是一个非常强大的系统调用,用于将文件或设备的内存区域映射到进程的地址空间中。这允许进程像访问普通内存一样访问文件内容,而不需要显式地读写文件。

  • munmap 用于取消之前通过 mmap 创建的内存映射。

  • mprotect 用于修改已映射内存区域的保护属性。

  • 我们可以用 pmap 命令查看进程的地址空间

    • (它是怎么实现的?)

3.3. 使用 mmap

Example 1: 申请大量内存空间

  • 瞬间完成内存分配

    • mmap/munmap 为 malloc/free 提供了机制

    • libc 的大 malloc 会直接调用一次 mmap 实现

  • 不妨 strace/gdb 看一下

Example 2: Everything is a file

  • 映射大文件、只访问其中的一小部分

# 打开文件
with open('/dev/sda', 'rb') as fp:
    # 映射文件
    mm = mmap.mmap(fp.fileno(),
                   prot=mmap.PROT_READ, length=128 << 30)
    # 打印文件前 512 字节
    hexdump.hexdump(mm[:512])

mmap 本质是一个 syscalls,用于将一个文件或设备的内存区域映射到进程的地址空间中。简单来说,它允许你像访问普通内存一样访问文件内容,而不需要显式地读写文件。

  • 高效处理大文件

    • 按需加载

    • 减少 I/O 操作

  • 高效内存分配

    • 瞬间完成内存分配

      • 操作系统不会真的分配内存

      • 只是做一个映射?

      • AI:mmap 申请内存时,不会立即分配物理内存,只有在实际访问时才会分配,因此申请大量内存时非常高效

    • 与 malloc 的关系

  • 内存保护

    • 设置保护属性

老师上课尽量多的进行了示例演示...

对于 mmap 的行为我自己的理解就是,在内存空间上 绑定一些 文件,通过记录文件路径...通过 AI 还需要注意的就是映射的回收 munmap

尽量多的学习 AI 的 best practice...当然有些 OI 竞赛的高血压代码 LLM 有可能也会输出...这时候要批判看待...

4. 入侵进程的地址空间

4.1. Hacking Address Space

进程 (状态机) 在 “无情执行指令机器” 上执行

  • 状态机是一个封闭世界

  • 但如果允许一个进程对其他进程的地址空间有访问权

    • 意味着可以任意改变另一个程序的行为

      • 听起来就很 cool

    • 例子:我们可以改变 gdb 或 其他编译程序的内部代码

      • 可以让你获得开挂权?...

      • 如下

“入侵” 进程地址空间的例子

  • 调试器 (gdb)

    • gdb 可以任意观测和修改程序的状态

4.2. 物理入侵进程地址空间

金手指:直接物理劫持内存

  • 听起来很离谱,但 “卡带机” 时代的确可以做到!

    • 可以通过直接操作硬件来读取或修改内存内容

    • 这种技术在现代系统中仍然存在,但通常需要特定的硬件支持或内核权限。

  • 今天我们有 Debug Registers 和 Intel Processor Trace

    • 调试寄存器是 CPU 提供的一种机制,用于支持调试功能。

    • Intel 处理器提供的一种硬件跟踪功能,可以记录程序的执行路径和内存访问情况。

  • 帮助系统工具 “合法入侵” 地址空间

    • :通过调试器或性能分析工具,合法地访问和修改进程的地址空间。

4.3. 物理入侵进程地址空间

Game Genie: 一个 Look-up Table (LUT)

4.4. 随着游戏越来越大

地址空间太大了,分不清哪些才是玩家属性...

  • 包含动态分配的内存,每次地址都不一样

  • 思路:Everything is a state machine

    • 观察状态机的 trace,就知道哪个是金钱了

查找 + Filter

  • 进入游戏时 exp=4950exp=4950

  • 打了个怪 exp=5100exp=5100

  • 符合 4950→51004950→5100 变化的内存地址是很少

    • 好了,出门就是满级了

4.5. 脚本的开始...

如果有一天可以接触,也是蛮有意思的...

你只要认为一件事情可以干,这件事情就可以干下去,尽管这件事情网上没有先例,没有教程...

AI 会助你完成这一切...

4.6. “外挂”

采集视频信号

  • 采集卡 (MS2130) + 树莓派 = 外挂

用魔法打败魔法

  • 创造变得前所未有的容易

我能否用 FPGA 设计一个电路,收到视频流,以及来自 I/O 端口的 bound-box 绘图指令,把 bound-box 绘制到视频流上输出。

总的来说是发挥想象力...看你想得到什么,然后就可以实现,换个思路,也许就会改变人类的命运...

Last updated