macOS Universal binaries & Mach-O Format
基本信息
Mac OS二进制文件通常被编译为通用二进制文件。通用二进制文件可以在同一个文件中支持多种架构。
这些二进制文件遵循Mach-O结构,基本上由以下部分组成:
头部
装载命令
数据

Fat Header
使用以下命令搜索文件:mdfind fat.h | grep -i mach-o | grep -E "fat.h$"
头部包含魔数字节,后面是文件包含的架构数(nfat_arch),每个架构都将有一个fat_arch结构体。
使用以下命令检查:
或使用Mach-O View工具:

正如您可能想到的,通常为2种架构编译的通用二进制文件会使大小翻倍,而为单个架构编译的二进制文件。
Mach-O头部
头部包含有关文件的基本信息,例如用于识别其为Mach-O文件的魔数字节以及有关目标架构的信息。您可以在以下位置找到它:mdfind loader.h | grep -i mach-o | grep -E "loader.h$"
Mach-O 文件类型
有不同的文件类型,你可以在这里的源代码中找到它们的定义。最重要的类型包括:
MH_OBJECT:可重定位目标文件(编译的中间产品,还不是可执行文件)。MH_EXECUTE:可执行文件。MH_FVMLIB:固定虚拟内存库文件。MH_CORE:代码转储。MH_PRELOAD:预加载的可执行文件(在 XNU 中不再支持)。MH_DYLIB:动态库。MH_DYLINKER:动态链接器。MH_BUNDLE:"插件文件"。使用 -bundle 在 gcc 中生成,并由NSBundle或dlopen显式加载。MH_DYSM:配套的.dSym文件(带有用于调试的符号的文件)。MH_KEXT_BUNDLE:内核扩展。
或者使用Mach-O View:

Mach-O 标志
源代码还定义了几个对加载库有用的标志:
MH_NOUNDEFS: 没有未定义的引用(完全链接)MH_DYLDLINK: Dyld 链接MH_PREBOUND: 动态引用预绑定。MH_SPLIT_SEGS: 文件分割为只读和读写段。MH_WEAK_DEFINES: 二进制文件具有弱定义的符号MH_BINDS_TO_WEAK: 二进制文件使用弱符号MH_ALLOW_STACK_EXECUTION: 使堆栈可执行MH_NO_REEXPORTED_DYLIBS: 库没有 LC_REEXPORT 命令MH_PIE: 位置无关可执行文件MH_HAS_TLV_DESCRIPTORS: 存在具有线程本地变量的部分MH_NO_HEAP_EXECUTION: 堆/数据页面不执行MH_HAS_OBJC: 二进制文件具有 Objective-C 部分MH_SIM_SUPPORT: 模拟器支持MH_DYLIB_IN_CACHE: 在共享库缓存中使用的 dylibs/frameworks。
Mach-O 加载命令
在这里指定了文件在内存中的布局,详细说明了符号表的位置,执行开始时主线程的上下文以及所需的共享库。提供了有关二进制文件加载到内存中的动态加载器 (dyld) 的指令。
使用了在上述提到的 loader.h 中定义的 load_command 结构。
有大约50种不同类型的加载命令,系统会以不同方式处理。最常见的是:LC_SEGMENT_64、LC_LOAD_DYLINKER、LC_MAIN、LC_LOAD_DYLIB和LC_CODE_SIGNATURE。
LC_SEGMENT/LC_SEGMENT_64
基本上,这种类型的加载命令定义了在执行二进制文件时,根据数据部分中指示的偏移量,如何加载__TEXT(可执行代码)和__DATA(进程数据)段。
这些命令定义了在执行过程中映射到进程的虚拟内存空间中的段。
有不同类型的段,比如**__TEXT段,保存程序的可执行代码,以及__DATA段,包含进程使用的数据。这些段位于Mach-O文件的数据部分**中。
每个段可以进一步划分为多个区块。加载命令结构包含了关于各自段内的这些区块的信息。
在头部首先找到段头:
段头的示例:

此头部定义了在其后出现的区块头的数量:
章节标题示例:

如果您将节偏移量(0x37DC)与arch开始的偏移量相加,在本例中为0x18000 --> 0x37DC + 0x18000 = 0x1B7DC

还可以通过命令行获取头部信息。
LC_CODE_SIGNATURE
LC_CODE_SIGNATURE包含有关 Mach-O 文件的代码签名的信息。它只包含一个指向签名 blob的偏移量。这通常位于文件的末尾。 但是,您可以在此博客文章和这个gists中找到关于此部分的一些信息。
LC_ENCRYPTION_INFO[_64]
LC_ENCRYPTION_INFO[_64]支持二进制加密。但是,当然,如果攻击者设法 compromise 进程,他将能够以未加密的方式 dump 内存。
LC_LOAD_DYLINKER
LC_LOAD_DYLINKER包含动态链接器可执行文件的路径,将共享库映射到进程地址空间。值始终设置为 /usr/lib/dyld。重要的是要注意,在 macOS 中,dylib 映射发生在用户模式,而不是内核模式。
LC_IDENT
LC_IDENT已过时,但当配置为在 panic 时生成 dumps 时,将创建一个 Mach-O 核心 dump,并在 LC_IDENT 命令中设置内核版本。
LC_UUID
LC_UUID随机 UUID。它本身对任何直接用途都没有用,但 XNU 会将其与进程信息的其余部分一起缓存。它可用于崩溃报告。
LC_DYLD_ENVIRONMENT
LC_DYLD_ENVIRONMENT允许在进程执行之前指定 dyld 的环境变量。这可能非常危险,因为它可以允许在进程内部执行任意代码,因此此加载命令仅在使用 #define SUPPORT_LC_DYLD_ENVIRONMENT 构建的 dyld 中使用,并进一步限制处理仅限于形式为 DYLD_..._PATH 的变量,指定加载路径。
LC_LOAD_DYLIB
LC_LOAD_DYLIB此加载命令描述了动态库依赖项,指示加载器(dyld)加载和链接该库。Mach-O 二进制文件所需的每个库都有一个 LC_LOAD_DYLIB 加载命令。
此加载命令是**
dylib_command**类型的结构(其中包含一个描述实际依赖动态库的 struct dylib):

您也可以通过以下命令行获取此信息:
一些潜在的与恶意软件相关的库包括:
DiskArbitration:监控 USB 驱动器
AVFoundation:捕获音频和视频
CoreWLAN:Wifi 扫描。
Mach-O 数据
文件的核心是数据区域,由加载命令区域中定义的几个段组成。每个段中可以包含各种数据部分,每个部分包含特定类型的代码或数据。
数据基本上是包含在加载命令LC_SEGMENTS_64中加载的所有信息的部分。

这包括:
函数表:包含有关程序函数的信息。
符号表:包含有关二进制文件使用的外部函数的信息
还可以包含内部函数、变量名称等等。
要检查它,您可以使用Mach-O View工具:

或者从命令行界面:
最后更新于