14 KiB
macOS通用二进制文件和Mach-O格式
从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS红队专家)!
支持HackTricks的其他方式:
- 如果您想看到您的公司在HackTricks中做广告或下载PDF格式的HackTricks,请查看订阅计划!
- 获取官方PEASS & HackTricks周边产品
- 探索PEASS家族,我们的独家NFTs收藏品
- 加入 💬 Discord群 或 电报群 或在Twitter上关注我们 🐦 @carlospolopm。
- 通过向HackTricks和HackTricks Cloud github仓库提交PR来分享您的黑客技巧。
基本信息
Mac OS二进制文件通常被编译为通用二进制文件。通用二进制文件可以在同一文件中支持多种架构。
这些二进制文件遵循Mach-O结构,基本上由以下部分组成:
- 头部
- 装载命令
- 数据
Fat Header
使用以下命令搜索文件:mdfind fat.h | grep -i mach-o | grep -E "fat.h$"
#define FAT_MAGIC 0xcafebabe
#define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */
struct fat_header {
uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */
uint32_t nfat_arch; /* 后面跟随的结构体数量 */
};
struct fat_arch {
cpu_type_t cputype; /* CPU指定器(int) */
cpu_subtype_t cpusubtype; /* 机器指定器(int) */
uint32_t offset; /* 指向该目标文件的文件偏移量 */
uint32_t size; /* 该目标文件的大小 */
uint32_t align; /* 2的幂对齐 */
};
头部包含魔术字节,后面是文件包含的架构数(nfat_arch
),每个架构都将有一个fat_arch
结构体。
使用以下命令检查:
% file /bin/ls
/bin/ls: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/bin/ls (for architecture x86_64): Mach-O 64-bit executable x86_64
/bin/ls (for architecture arm64e): Mach-O 64-bit executable arm64e
% otool -f -v /bin/ls
Fat headers
fat_magic FAT_MAGIC
nfat_arch 2
architecture x86_64
cputype CPU_TYPE_X86_64
cpusubtype CPU_SUBTYPE_X86_64_ALL
capabilities 0x0
offset 16384
size 72896
align 2^14 (16384)
architecture arm64e
cputype CPU_TYPE_ARM64
cpusubtype CPU_SUBTYPE_ARM64E
capabilities PTR_AUTH_VERSION USERSPACE 0
offset 98304
size 88816
align 2^14 (16384)
或使用Mach-O View工具:
正如您可能在想的那样,通常为2种架构编译的通用二进制文件会使大小翻倍,而为单个架构编译的二进制文件。
Mach-O头部
头部包含有关文件的基本信息,例如用于识别其为Mach-O文件的魔术字节以及有关目标架构的信息。您可以在以下位置找到它:mdfind loader.h | grep -i mach-o | grep -E "loader.h$"
#define MH_MAGIC 0xfeedface /* the mach magic number */
#define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */
struct mach_header {
uint32_t magic; /* mach magic number identifier */
cpu_type_t cputype; /* cpu specifier (e.g. I386) */
cpu_subtype_t cpusubtype; /* machine specifier */
uint32_t filetype; /* type of file (usage and alignment for the file) */
uint32_t ncmds; /* number of load commands */
uint32_t sizeofcmds; /* the size of all the load commands */
uint32_t flags; /* flags */
};
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
struct mach_header_64 {
uint32_t magic; /* mach magic number identifier */
int32_t cputype; /* cpu specifier */
int32_t cpusubtype; /* machine specifier */
uint32_t filetype; /* type of file */
uint32_t ncmds; /* number of load commands */
uint32_t sizeofcmds; /* the size of all the load commands */
uint32_t flags; /* flags */
uint32_t reserved; /* reserved */
};
文件类型:
- MH_EXECUTE (0x2):标准的 Mach-O 可执行文件
- MH_DYLIB (0x6):Mach-O 动态链接库(即 .dylib)
- MH_BUNDLE (0x8):Mach-O bundle(即 .bundle)
# Checking the mac header of a binary
otool -arch arm64e -hv /bin/ls
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 ARM64 E USR00 EXECUTE 19 1728 NOUNDEFS DYLDLINK TWOLEVEL PIE
或者使用Mach-O View:
Mach-O Load commands
文件在内存中的布局在这里指定,详细说明了符号表的位置,执行开始时主线程的上下文以及所需的共享库。提供了有关二进制文件加载到内存中的动态加载器**(dyld)**的指令。
使用了在上述**loader.h
中定义的load_command**结构。
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
系统处理大约50种不同类型的加载命令。最常见的是:LC_SEGMENT_64
、LC_LOAD_DYLINKER
、LC_MAIN
、LC_LOAD_DYLIB
和LC_CODE_SIGNATURE
。
LC_SEGMENT/LC_SEGMENT_64
{% hint style="success" %} 基本上,这种类型的加载命令定义了在执行二进制文件时,根据数据部分中指示的偏移量,如何加载__TEXT(可执行代码)和__DATA(进程数据)段。 {% endhint %}
这些命令定义了在执行过程中映射到进程的虚拟内存空间中的段。
有不同类型的段,比如**__TEXT段,保存程序的可执行代码,以及__DATA段,包含进程使用的数据。这些段位于Mach-O文件的数据部分**中。
每个段可以进一步划分为多个区块。加载命令结构包含了关于各自段内的这些区块的信息。
在头部首先找到段头:
struct segment_command_64 { /* for 64-bit architectures */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* includes sizeof section_64 structs */
char segname[16]; /* segment name */
uint64_t vmaddr; /* memory address of this segment */
uint64_t vmsize; /* memory size of this segment */
uint64_t fileoff; /* file offset of this segment */
uint64_t filesize; /* amount to map from the file */
int32_t maxprot; /* maximum VM protection */
int32_t initprot; /* initial VM protection */
uint32_t nsects; /* number of sections in segment */
uint32_t flags; /* flags */
};
段头的示例:
此头部定义了在其后出现的区块头的数量:
struct section_64 { /* for 64-bit architectures */
char sectname[16]; /* name of this section */
char segname[16]; /* segment this section goes in */
uint64_t addr; /* memory address of this section */
uint64_t size; /* size in bytes of this section */
uint32_t offset; /* file offset of this section */
uint32_t align; /* section alignment (power of 2) */
uint32_t reloff; /* file offset of relocation entries */
uint32_t nreloc; /* number of relocation entries */
uint32_t flags; /* flags (section type and attributes)*/
uint32_t reserved1; /* reserved (for offset or index) */
uint32_t reserved2; /* reserved (for count or sizeof) */
uint32_t reserved3; /* reserved */
};
章节标题示例:
如果您添加 部分偏移量(0x37DC)+ arch 开始的偏移量,在这种情况下为 0x18000
--> 0x37DC + 0x18000 = 0x1B7DC
也可以通过命令行获取头信息。
otool -lv /bin/ls
以下是由此命令加载的常见段:
__PAGEZERO
: 它指示内核映射地址零,因此无法从中读取、写入或执行。结构中的maxprot和minprot变量设置为零,表示此页面上没有读写执行权限。- 此分配对于缓解空指针解引用漏洞很重要。
__TEXT
: 包含具有读取和执行权限(不可写)的可执行代码。此段的常见部分:__text
:已编译的二进制代码__const
:常量数据__cstring
:字符串常量__stubs
和__stubs_helper
:在动态库加载过程中涉及
__DATA
: 包含可读和可写的数据(不可执行)。__data
:已初始化的全局变量__bss
:未初始化的静态变量__objc_*
(__objc_classlist、__objc_protolist等):Objective-C运行时使用的信息
__LINKEDIT
: 包含链接器(dyld)的信息,如“符号、字符串和重定位表条目”。__OBJC
: 包含Objective-C运行时使用的信息。尽管此信息也可能在__DATA段中的各种__objc_*部分中找到。
LC_MAIN
包含entryoff属性中的入口点。在加载时,dyld只需将此值添加到二进制文件的(内存中的)基址,然后跳转到此指令以开始执行二进制代码。
LC_CODE_SIGNATURE
包含有关Macho-O文件代码签名的信息。它只包含一个指向签名块的偏移量。这通常位于文件的末尾。
但是,您可以在此博客文章和这个gists中找到有关此部分的一些信息。
LC_LOAD_DYLINKER
包含动态链接器可执行文件的路径,将共享库映射到进程地址空间。值始终设置为/usr/lib/dyld
。重要的是要注意,在macOS中,dylib映射发生在用户模式而不是内核模式中。
LC_LOAD_DYLIB
此加载命令描述了一个动态库依赖项,指示加载器(dyld)加载和链接该库。Mach-O二进制文件所需的每个库都有一个LC_LOAD_DYLIB加载命令。
- 此加载命令是**
dylib_command
**类型的结构(包含描述实际依赖动态库的struct dylib):
struct dylib_command {
uint32_t cmd; /* LC_LOAD_{,WEAK_}DYLIB */
uint32_t cmdsize; /* includes pathname string */
struct dylib dylib; /* the library identification */
};
struct dylib {
union lc_str name; /* library's path name */
uint32_t timestamp; /* library's build time stamp */
uint32_t current_version; /* library's current version number */
uint32_t compatibility_version; /* library's compatibility vers number*/
};
您也可以通过命令行获得此信息:
otool -L /bin/ls
/bin/ls:
/usr/lib/libutil.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
一些潜在的与恶意软件相关的库包括:
- DiskArbitration:监控USB驱动器
- AVFoundation:捕获音频和视频
- CoreWLAN:Wifi扫描。
{% hint style="info" %}
Mach-O二进制文件可以包含一个或多个构造函数,这些函数将在LC_MAIN中指定的地址之前执行。
任何构造函数的偏移量都保存在**__DATA_CONST段的__mod_init_func**部分中。
{% endhint %}
Mach-O数据
文件的核心是数据区域,由加载命令区域中定义的几个段组成。每个段中可以包含各种数据部分,每个部分保存特定类型的代码或数据。
{% hint style="success" %} 数据基本上是包含在加载命令LC_SEGMENTS_64中加载的所有信息的部分。 {% endhint %}
这包括:
- 函数表:保存有关程序函数的信息。
- 符号表:包含二进制文件使用的外部函数的信息
- 还可能包含内部函数、变量名称等等。
要检查它,您可以使用Mach-O View工具:
或者从命令行界面:
size -m /bin/ls
从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS Red Team Expert)!
其他支持HackTricks的方式:
- 如果您想在HackTricks中看到您的公司广告或下载PDF格式的HackTricks,请查看订阅计划!
- 获取官方PEASS & HackTricks周边产品
- 探索PEASS家族,我们的独家NFTs
- 加入 💬 Discord群 或 电报群 或 关注我们的Twitter 🐦 @carlospolopm。
- 通过向HackTricks和HackTricks Cloud github仓库提交PR来分享您的黑客技巧。