-
-
[原创]Apple HFS+文件系统可视化分析
-
发表于: 2024-6-15 11:52 2744
-
起因
为了编译和调试XNU内核,需要修改安装镜像文件BaseSystem.img。然而,OpenCore对HFS+的支持存在问题,导致在修改镜像后无法读取BootKernelExtensions.kc文件。因此,需要深入了解HFS+文件系统,才能解决OpenCore的相关问题。
HFS+是一个较老的文件系统,目前macOS默认使用的是APFS,但由于HFS+结构简单且轻量,仍被保留在系统安装盘中。HFS+有官方开源项目,但由于其为内核模块且代码较为复杂,直接从源码分析并不容易,最好编写一些demo程序进行辅助理解。
注意:HFS+由于历史上支持PowerPC架构,使用的是大端序(Big-endian),文件名也是大端序的Unicode格式。
*HFS+是为机械硬盘设计的文件系统,APFS 是苹果为SSD和现代存储设备设计的文件系统,HFS+在械硬盘上还是不错的选择(磁道上顺序读写更快).
环境
- macOS
- 010 Editor + Drive Template
- HFS源码
- ChatGPT/GitHub Copilot/阿里云通义
磁盘镜像
- 下载macOS安装镜像BaseSystem.dmg转BaseSystem.img
- 使用磁盘管理创建一个dmg
GPT分区和HFS分区头结构
镜像是一个GPT分区, 比较简单,可以通过找到HFS+的FirstLBA来定位和解析HFS+分区。可以利用ChatGPT直接生成解析代码。
HFS+分区头:源码
以上结构先不需要详细了解,可以先看以下图表后续再理解。
BTree索引结构
需要理解的专业名词
- Tree和BTree
- 二分查找 (Binary Search)
- 复合索引 (Composite Index)
结构简介
首先需要了解BTree的索引结构,所有的文件和目录查找都通过BTree进行。BTree的高度是动态调整的,文件越多,BTree树越高。同一层高度的节点(Node)也是相互前后链接的。
文件检索
检索是通过文件和目录的父层ID和文件名进行的(复合索引[parentId, Name])。每个Index Node都有子层的最开始的目录ID和最小的文件名(子层可以是末端叶节点(Leaf),也可以是子Index)。目录ID和文件名都按从小到大的顺序存放。
如果一个Index Node下仍然是另一个Index Node,它包含的是子Index的最小父目录ID和最小文件名。对于较大的目录,子Index可能有多个相同的父目录ID,所以需要文件名且按顺序排列。通过从根节点到叶节点的逐层二分查找,最终找到目标文件。
Node结构
Index和Leaf Node的结构是统一的,HFS+尽量复用和统一结构。Record的结构由Descriptor中的Kind确定,每个Record的起始位置由索引决定(注意:由于文件名长度不一,每个Record的大小不固定。如果文件名固定最大长度为255,会浪费较多空间)。
Index和Leaf Node使用时不是先用满一个Node再使用下一个,而是先使用大概20%的容量,以便在添加和删除记录时尽量不需要修改多个Node。本文章只大概介绍结构,修改的逻辑涉及更深的算法,仅略微带过。
文件结构
每个文件都有一个ForkData,每个ForkData包含8个Extent。每个Extent指向具体的块(startBlock)和块的数量(blockCount)。如果分区碎片化严重,超过8个Extent时需要在Extents BTree中检索相应的ForkData。
文件系统的碎片化问题是所有文件系统都无法避免的,HFS+碎片化严重时需要更多的扩展ForkData,导致读取文件效率下降。HFS+的设计理念似乎是优先使用前面的空闲块,这个设计不太合理。欢迎有了解的朋友交流讨论。
OpenCore OpenHfsPlus模块问题
要解决最开始提到的OpenCore的OpenHfsPlus模块的问题,实际并不是问题,而是作者没有实现读取超过默认8个Extent(作者称其为分片fragments)。作者在注释中提到未实现此功能。相关代码:
1 2 3 4 5 6 7 8 9 10 11 | static fsw_status_t fsw_hfsplus_get_ext( struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d, struct fsw_extent *e) { // ... // FIXME: more than 8 fragments not yet supported! FSW_MSG_ASSERT((FSW_MSGSTR( "FswHfsPlus: get_ext: got_here\n" ))); return FSW_UNSUPPORTED; } |
深入了解
要全面了解HFS+,需要完整阅读HFS源码,并理解XNU系统,因为HFS+只是内核驱动的一部分,需要依赖内核的缓存系统、线程锁系统等。HFS+的管理逻辑还涉及插入删除时索引的重建、BTree高度调整等,以及确保多线程下文件ID的唯一性等。
~
黑苹果, XNU内核交流可QQ: 3=1=0=4=1=9=0=6=2 (倒序)
~
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)