Home avatar

AFL 源码阅读——part 2 afl-tmin、afl-showmap 和 afl-analyze

通过这一部分代码可以看到,共享内存里面存放的 bitmap 数据是怎么被用来统计覆盖率的。

这是一个用来精简输入样例的工具 (trimming)。

  • 为什么需要精简输入

大的输入执行起来速度比较慢,而且如果文件太大了,那么编译更可能发生在不重要的部分。

有两种模式来进行精简,Non-Crashing Input Mode 能够在路径完全相同的情况下尽量减少输入大小,而 Crashing Input Mode 可以精简能够制造 crash 的 poc 代码。

Crashing Input Mode Non-Crashing Input Mode
目的 找到可以稳定复现崩溃的文件,不在乎路径是否变化 找到 保持执行路径完全相同的更小文件
例子 将 10 MB 的崩溃 PoC 文件精简为 100 字节,仍能触发同一漏洞 将 1 MB 的 JPEG 测试用例精简为 50 KB,且仍覆盖相同的图像解析逻辑
是否需要插桩 不用插桩 需要

不仅仅是 afl-tmin,afl-fuzz 也内置了精简器:以一些固定长度尝试删除数据块,如果删除之后的路径 checksum 保持不变,就认为可以删减。

tmin 采用四阶段算法来删减数据,四个阶段删减数据的粒度从大到小,从删减粗粒度数据,到删减极细的字节大小的粒度,达到 trimming 的效果。

  1. 粗粒度归零

大刀阔斧简化数据,使用大块步长将数据归零,这是为了减少后续细粒度操作次数。

  1. 逐级细化删除

递减的块长和步长进行块删除,像「二分查找」一样,不断缩小切割范围,直到无法再切。

  1. 字母表归一化

统计唯一字符,尝试批量替换为 '0'(ASCII 字符 0x30),'0' 在文本格式中(如配置文件、日志)通常不会破坏结构

rcore lab5&6 记录

新增了一个 shell。在此之前,程序时作为 “task” 存在的,而从这章开始,引入了进程的概念,不必按一定顺序依次执行所有程序了。

在之前的结构体的基础上,进行了一些变化:

结构体/模块名 变化/新增内容说明 作用
PidHandle 新增,用于唯一标识每个进程,并通过 PidAllocator 管理 提供进程的唯一标识和资源管理
PidAllocator 新增,负责分配和回收 PID 这是一个分配器,类似之前的 FrameAllocator
KernelStack 新增 每个进程有独立的内核栈以隔离执行上下文。RAII 自动回收物理页面。
TaskControlBlock 扩展,添加进程相关信息,如地址空间、文件表等 描述完整进程信息(不再只是一个“任务”)
TaskManager 拓展功能,从原本的简单任务队列演变为支持多进程调度、切换 全局调度器,支持并发进程管理
Processor 拓展,记录当前正在运行的进程(而非仅任务) 支持多核处理器上的多进程调度
由于本章需要进行进程之间的切换与调度,所以设计了 pid 机制来标识不同的进程,这些不同的进程需要不同的内核栈才能保证在上下文切换的时候不混乱(下文解释了)。为了保存进程信息,去修改了之前的 Task 管理器,并且拆分了进程调度的部分为一个新的结构体 Processor
内核栈用来做什么

本章新增里一个结构体是内核栈,在此之前,是没有给不同进程单独分配“内核栈”的做法的,因为之前的进程按照顺序执行,而从这章开始,涉及不同进程的调度和切换,如果两个进程都需要陷入内核的话,只有一个内核栈就会产生冲突了。

AFL 源码阅读——part 1 插桩

相关的资料有:

整个 AFL 项目的主要部分在 afl-fuzz.c 文件里面,直接看这个文件非常庞大,不太易于阅读,所以可以从其他部分来看,先去了解其 API 的使用方式。

先问 ai 总结了一下功能相关的文件:

功能 主要文件
GCC 插桩 afl-gcc.c, afl-as.c, afl-as.h
Clang/LLVM 插桩 llvm_mode/afl-clang-fast.c, llvm_mode/afl-llvm-pass.so.cc
Fuzz 变异策略 afl-fuzz.cbitfliphavoc 等函数)
输入传递 afl-fuzz.cwrite_to_testcase()
覆盖度收集 插桩代码(如 afl-as.h 模板)、afl-fuzz.c 的覆盖率分析逻辑
QEMU 模式 qemu_mode/afl-qemu-cpu-inl.h、QEMU 修改的 cpu-exec.c

参考了 AFL源码阅读(一):启程 的阅读顺序:

阅读源码的主要目标应该是:

  1. 理清静态插桩过程(gcc、clang、llvm mode)
  2. 理清 fuzz 过程:如何变异、如何将 input 传递给程序、如何收集覆盖度信息
  3. 理清 qemu mode 的插桩和执行过程

因此,我们决定阅读顺序:

  1. 阅读 afl-gcc.cafl-as.c,即静态插桩相关代码
  2. 阅读 afl-tmin.c ,这个工具的用途是「将一个 input case 缩小,但与原 input 拥有相同的覆盖度」。它会完整地演示如何收集程序的覆盖度信息,而不涉及 afl-fuzz.c 中的其他流程。这将给我们提供一个绝佳的切面,以研究 AFL 收集覆盖度的方法
  3. 阅读 afl-fuzz.c

根据这个文件的注释说明,afl-gcc 是 gcc 或者 clang 的简易包装。在这个文件的 main 函数中,除去检查参数的部分,主要就是 3 行代码,用于找到 afl-as (这个是插桩的工具),编辑传递给 gcc 的参数,使用 execvp 来启动 gcc 进程:

rcore lab4 记录

“本章中内核将实现虚拟内存机制,这注定是一趟艰难的旅程。”

在本章中,出现了虚拟地址和物理地址的转换,整个过程中的关键部分是几个结构体。

MMU

不论是内核还是用户态的地址,都需要经过 MMU 的转换,只不过内核是直接映射了,虚拟地址=物理地址,但是用户态的虚拟地址是需要找到对应的实际物理地址的。

摸一个基于fastapi的飞书bot

image-20250401163104226

查看飞书的介绍文档,有两种机器人,一种是自定义机器人,只能推送消息,没办法和用户互动;应用机器人可以互动,功能也更多一些,不过需要企业的管理员审核。最好的方式是自己的企业创建一个企业应用,功能比个人的应用更多,而且 5 个人一下不用审核。

预计写一个打卡 bot,可以和数据库以及 deepseek 交互。因为涉及数据转换比较复杂,决定使用 python fastapi + sqlite 吧。用 python 写代码比较舒适区内…虽然官方有 sdk 封装好的,但是我寻思着,如果后续有写成前后端的需要的话,单单用 fastapi 不用 sdk 更好兼容一些,所以这次就没有使用官方的 sdk,而是自己封装了 bot 的类。

用 fastapi 的体验还是很好,一些数据库的交互、测试等等过程都比较完善而且简洁,虽然在使用的过程中因为版本迭代的原因,有一些用法我感觉还挺好用,但是 deprecated 了()

既然要部署在服务器上,就不能偷懒不用包管理了(),所以这次选了 poetry(本来在 pdm 和 poetry 之间纠结,然后觉得 poetry 更好听就选它了)Poetry。创建程序用 poetry new project_name; poetry install 添加包用 poetry add pkg_name 方便嘞~

fastapi 的文档一看就懂,比如在 fastapi 里面和 sqlite 交互:SQL (Relational) Databases - FastAPI,这里用到了 SQLModel 使用 Python 对象和数据库交互,就不用写 sql 语言啦~~ 支持这些类型:Pydantic field

rustlings 学习笔记

为了在 rustlings 里面用 rust analyzer,可以运行 rustlings lsp 命令,这样会生成 rust-project.json 文件提供一些 crate 信息,lsp 就可以使用了~

这里记录一下写的不是很优雅的地方可以怎么改进,以及一些理解。

while let 很适合用于完成遍历数组的工作,比如

安卓:frida hook 和 ida 调试 so 库的环境配置

按照这个顺序来布置一下安卓环境

  1. 编译一个安卓程序:带 native 层的
  2. 配置 frida 环境调试程序
  3. 配置 ida 调试 native 层

感觉版本还是很魔幻的,有些问题换了一个版本就莫名奇妙好了。

如果 app install 没有让 app 在桌面显示:adb install问题

PAWNYABLE kernel ebpf 笔记

这里 pwn ebpf 的逻辑是让 verifier 对数据的范围估算错误,从而越界访问或者有任意读写的效果。

eBPF Docs ebpf 的自己的文档

BPF数据传递的桥梁——BPF Map(一) – My X Files

ebpf 的 maps 创建之后返回一个 fd,因此可以在 user 和 kernel 的空间访问这个 map 里面的数据。

字节码可以参考 【译】eBPF 概述:第 2 部分:机器和字节码 | Head First eBPF

可以不用 c 来写一个 ebpf 程序,能直接用宏来写 ebpf 的机器码,比如 linux/samples/bpf/bpf_insn.h 里面定义的

PAWNYABLE kernel FUSE 笔记

userfaultfd 可以在驱动读写数据的时候,触发中断,切换到用户空间。此时可以将数据释放,释放的内存被其他结构体占有,然后就能进行 uaf_read 或者 uaf_write,。

自 Linux kernel 5.11 版本起,非特权用户被禁止使用 userfaultfd 系统调用,但是我们仍能通过 FUSE 达成同样的效果(在用户空间实现文件系统的机制,本身不是一个文件系统)。

fuse 的文件系统包括 CephFS、GlusterFS、ZFS、sshfs、mailfs。访问 FUSE 的文件系统,可以使用和内核文件系统相同的接口。

实现主要由三个部分组成