拓冰建站拓冰建站
首页 / 资讯中心 / 正文

radare2 的 SIOL 简单 IO 层:Maps、Sections 变换与 Emulation 内存映射架构解析

radare2 的 SIOL 简单 IO 层Maps、Sections 变换与 Emulation 内存映射架构解析【免费下载链接】radare2UNIX-like reverse engineering framework and command-line toolset项目地址: https://gitcode.com/gh_mirrors/ra/radare2导读SIOLSimple IO Layer是 radare2 中负责将二进制文件、内存缓存与虚拟地址空间统一为可寻址视图的 IO 抽象层设计文档。它定义了 Write-Mask、Buffer、Cache、Maps、Descs 与 Plugin 的分层结构并解释了 Hexeditor十六进制编辑、Analysis静态分析与 Emulation仿真执行三种场景下 Section 到 Map 的变换规则。读完本文你将掌握 RIO 层 Map/Desc 的职责划分、mapid 的引用约定、三种变换算法的逐步推演以及在 libr/io 源码中对应的实现载体能够直接用于理解 radare2 的内存映射加载与调试器初始化流程。SIOL 的顶层分层视图文档用一张自下而上的 ASCII 图描述了 IO 层的完整栈结构。整理后从底层到顶层依次为 | Write-Mask | 写掩码按位与运算过滤写操作字节 | Buffer | 缓冲区文档作者认为可能已废弃暂无使用场景 | Cache (V) | 虚拟地址缓存层 | Maps | | Sections (transformed) | Section 变换为 Map | Descs | | | | Cache (P) | 物理地址缓存层per-desc | Plugin | 底层 IO 插件如 malloc、null 等 各层的职责与仓库中的对应实现如下Plugin最底层的数据来源通过RIOPlugin结构描述见 libr/include/r_io.h 中RIOPlugin定义提供open、read、write、seek、close等回调。仓库内置了malloc://、null://、mmap://、rap://、gdb://等数十种插件。Descs一个RIODesc见 libr/include/r_io.h代表一个已打开的数据源包含fd、perm、uri、plugin等字段所有描述符统一存放在RIO.filesRIDStorage中通过整数 fd 访问。每个 desc 还挂有自身的物理地址缓存Cache (P)。Maps虚拟地址空间的映射表一个 Map 把一个 desc 的某个区间映射到虚拟地址。RIO.maps是RIDStorage类型按 mapid 索引见 libr/include/r_io.h。Cache (V)位于 Maps 之上的虚拟缓存层对应RIO.cacheRIOCache结构含layers、mode、enabled字段用于在虚拟地址空间暂存写入、支持 undo/redo。Write-Mask写掩码见RIO.write_mask与write_mask_len字段。在 libr/io/io.c 的写入路径中若io-write_mask非空写入数据会先与掩码做按位与运算再落盘常用于模拟只读内存区。Buffer 层在文档中被明确标注maybe this could be deprecated, I see no usecase for the buffer属于设计者的备注而非现行功能因此不做展开。Maps以 mapid 为核心的地址区间抽象为什么用 mapid 而不是指针文档强调每个 Map 拥有唯一的 mapidRIO 外部的代码应当使用这个整数 id 而非指针来引用 Map。这样做的代价是查找多了一层间接r_id_storage_get按 id 检索但收益是避免了悬垂指针带来的稳定性风险——外部模块不再持有可能失效的RIOMap*。在源码中RIOMap结构见 libr/include/r_io.h包含了文档列出的全部映射信息字段字段含义id唯一映射 idmapid由r_id_storage_add分配fd关联的 desc 文件描述符delta物理偏移paddr vaddr - itv.addr deltaitv地址区间RInterval{addr, size}即虚拟地址区间perm/sperm实际权限 / 期望权限软权限ts时间戳配合RIOMapRef做弱引用校验overlay覆盖写红黑树用于不落盘的补丁写name映射名称如mmap.section映射的区间运算由头文件宏提供r_io_map_begin/r_io_map_from取起点r_io_map_to取终点r_itv_end(itv) - 1r_io_map_size取大小r_io_map_contain判断地址是否落在区间内。Map 的增删改查创建r_io_map_add(io, fd, perm, delta, addr, size)在 libr/io/io_map.c 实现。其内部限制map 的权限不能高于其所属 desc 的权限即map-perm desc-perm perm地址区间会与 fd 的实际大小取交集R_MIN(size, fd_size - delta)。r_io_map_add_bottom与之对称但把新 map 挂到 bank 底部低优先级。查询r_io_map_get按 id 取 mapr_io_map_get_at返回虚拟地址处优先级最高的 mapr_io_map_get_paddr按物理地址反查r_io_map_get_by_fd列出某 fd 关联的所有 map。删除r_io_map_del会从所有 bank 中移除该 map 引用再释放r_io_map_del_for_fd批量删除某 fd 的 map。重定位r_io_map_remap改变 map 的起始虚拟地址并同步更新所有 bank 中的边界。优先级r_io_map_priorize/r_io_map_depriorize将 map 在 bank 中置顶或置底影响地址冲突时的命中顺序。RIOBank 与 Map 引用文档中的 Maps 层在现代实现中经由RIOBank组织见 libr/io/io_bank.c。RIOBank内维护maprefsRIOMapRef列表含id与ts以及submaps红黑树。RIOMapRef是一种带时间戳的弱引用——r_io_map_get_by_ref会比较map-ts ref-ts时间戳不匹配说明 map 已被删除重建从而避免使用陈旧引用。这也是文档强调指针会伤害你的工程落地外部代码持有的实际上是(id, ts)这样的令牌。Section 变换三种目标与两条 map 关系Section 的映射信息从二进制加载器RBinSection到 IO 层的变换是 SIOL 的核心场景。Section 携带以下字段字段含义addr文件中的物理起始地址paddrsize文件中的大小vaddr加载后的虚拟地址vsize虚拟内存中的大小含 BSS 等零填充区fd所属文件描述符文档指出一个 Section 可以关联到两个 Map即memmap内存映射与filemap文件映射。当vsize size时文件无法覆盖完整的虚拟区间就需要用内存映射补足差值filemap记录文件数据的落点memmap记录补零内存的落点。Hexeditor 变换大小无需扩展的场景Hexeditor 目标是纯粹的查看/编辑模式算法如下检查addr ! vaddr若相等则无需变换直接可用以min(size, vsize)为大小创建 map把 fd 对应 desc 从addr起映射到vaddrfilemap设为该 map 的 idmemmap保持为 0。也就是说只要文件内容与虚拟地址对齐只需一张 map 就足以覆盖编辑需求。Analysis 变换vsize size 时的补零方案当vsize size时直接退化为 Hexeditor 变换否则走文件段 零填充段两步创建大小为size的 map把 fd 对应 desc 从addr映射到vaddrfilemap指向该 map用null 插件打开一个大小为vsize - size的新 desc再创建大小为vsize - size的 map把新 desc 从0x0映射到vaddr sizememmap指向该 map。零填充段的实现载体是null://插件见 libr/io/p/io_null.c它的__read用memset(buf, 0x00, ...)返回全零数据__write直接丢弃写入__resize可调整虚拟大小。因此 Analysis 模式下BSS 等需要加载器清零的区域被建模为读全零、写丢弃的虚拟 desc。这套逻辑在 libr/core/cbin.c 的add_section与io_create_mem_map中有完整落地当sec-vsize psizepsize 为文件内实际可读字节数时先映射文件部分大小psize再构造null://gap的 desc 并在addr psize处映射 gap 大小的零填充区最后把该 map 命名为mmap.section并复用已存在的同名 null desc 以避免重复分配findReusableFile。Emulation 变换可写内存的快照复制Emulation 目标面向仿真执行需要可写的虚拟内存算法如下当 Section 不允许写访问时退化为 Analysis 变换用malloc 插件以写权限打开大小为vsize的新 desc从 fd 所指 desc 的addr起复制min(size, vsize)字节到新 desc 的0x0处创建大小为vsize的 map把新 desc 从0x0映射到vaddr。malloc://插件见 libr/io/p/io_malloc.c在此扮演堆内存角色它用calloc分配缓冲区支持malloc://size、hex://hex、slurp://file、stdin://、hexfile://等 URI 形式__write直接修改缓冲区。仿真场景下文件内容先被快照进 malloc 缓冲区之后的写入全部发生在内存中而不会污染原始文件这正是r_io_open_at打开 desc 后执行r_io_map_add的标准流程见 libr/io/io.c。三种变换的对比目标触发条件使用插件复制行为filemap / memmapHexeditoraddr vaddr原 desc无复制filemap新 mapmemmap0Analysisvsize size原 desc null无复制补零filemap文件段memmap零段Emulation允许写malloc快照min(size,vsize)字节单一 malloc map从源码结构看三种变换的目标对象Hexeditor/Analysis/Emulation分别对应 radare2 的RCore在-w/普通打开、aaa分析、aaaESIL 仿真如 libr/esil/esil.c 中通过RIOBind的read_at/write_at访问 IO等不同工作流中对内存视图的差异化需求。从文档到代码SIOL 在 radare2 中的落地路径核心数据结构索引概念类型定义位置IO 总控RIOlibr/include/r_io.h描述符RIODesclibr/include/r_io.h映射RIOMaplibr/include/r_io.h映射弱引用RIOMapReflibr/include/r_io.hBankRIOBanklibr/include/r_io.hIO 插件接口RIOPluginlibr/include/r_io.h写掩码实现io-write_masklibr/io/io.cMap 操作实现r_io_map_*libr/io/io_map.cBank 操作实现r_io_bank_*libr/io/io_bank.cnull 插件r_io_plugin_nulllibr/io/p/io_null.cmalloc 插件r_io_plugin_malloclibr/io/p/io_malloc.cSection 到 Map 变换add_section/io_create_mem_maplibr/core/cbin.c初始化与打开流程RIO的生命周期从 libr/io/io.c 的r_io_new开始依次初始化 descr_io_desc_init、bankr_io_bank_init、mapr_io_map_init与 cacher_io_cache_init并创建一个名为default的 bank。打开文件时r_io_open_nomap只打开 desc 不建立映射r_io_open在打开 desc 后立即r_io_map_add(io, fd, perm, 0, 0, desc_size)把整个文件映射到虚拟地址 0x0r_io_open_at则把 desc 映射到指定地址at。读写在 libr/io/io.c 中经由r_io_read_at/r_io_write_at分发先通过 bank 的 submap 树定位命中的 map再换算到 desc 的物理偏移执行插件回调。写路径还会依次经过写掩码write_mask按位与与虚拟缓存RIOCache这正是 SIOL 顶层图中 Write-Mask → Cache → Maps 分层顺序的运行逻辑。实战验证在 r2 中观察 Map 变换用户可以通过 radare2 命令行直接观察 SIOL 的分层结果。以一个含 BSS 段的 ELF 为例$ r2 /bin/ls [0x00000000] o # 列出打开的 descfd、插件、uri、权限 [0x00000000] om # 列出当前 bank 的所有 mapmapid、区间、权限、fd [0x00000000] om~heap # 过滤查看与堆相关的元数据映射 [0x00000000] on null://4096 # 手动打开一个 4KB 的零填充 desc [0x00000000] om # 再次查看可看到新增的 null 映射其中om输出的每一行即一个RIOMap其 id 列就是文档强调的 mapido输出的 fd 与插件名则对应 Descs 层。加载二进制时iS输出的vsize大于size的 section如.bss就是 Analysis 变换的典型触发对象。总结SIOL 设计文档为 radare2 的 IO 层画出了清晰的职责边界Plugin 负责字节来源Desc 负责数据源生命周期Map 负责虚拟地址到物理数据的区间换算Cache 与 Write-Mask 负责写入语义的修饰而 Section 变换则把二进制格式中的段描述翻译为运行时所需的 map 组合。三种变换目标的递进关系Hexeditor → Analysis → Emulation本质上是对文件视图 → 补零内存视图 → 可写快照视图的渐进抽象在 libr/io 与 libr/core/cbin.c 的源码中均有对应实现可供对照阅读。理解 SIOL也就理解了 radare2 如何让十六进制编辑、静态分析与动态仿真共享同一套统一的内存寻址模型。【免费下载链接】radare2UNIX-like reverse engineering framework and command-line toolset项目地址: https://gitcode.com/gh_mirrors/ra/radare2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门