macOS Dyld Process
基本信息
Mach-o 二进制文件的真正入口点是动态链接器,在 LC_LOAD_DYLINKER 中定义,通常为 /usr/lib/dyld。
这个链接器需要定位所有可执行库,在内存中映射它们,并链接所有非懒加载库。只有在此过程完成后,二进制文件的入口点才会被执行。
当然,dyld 没有任何依赖(它使用系统调用和 libSystem 片段)。
如果此链接器包含任何漏洞,因为它在执行任何二进制文件(甚至是高度特权的二进制文件)之前被执行,将有可能提升权限。
流程
Dyld 将由 dyldboostrap::start 加载,它还会加载诸如栈保护之类的东西。这是因为此函数将在其**apple参数向量中接收此类敏感的值**。
dyls::_main() 是 dyld 的入口点,它的第一个任务是运行 configureProcessRestrictions(),通常会限制**DYLD_***环境变量,详细说明在:
然后,它映射 dyld 共享缓存,其中预链接了所有重要的系统库,然后映射二进制文件依赖的库,并递归继续,直到加载所有需要的库。因此:
它开始加载插入的库,使用
DYLD_INSERT_LIBRARIES(如果允许)然后是共享缓存中的库
然后是导入的库
然后继续递归导入库
一旦所有库都加载完毕,这些库的初始化程序将被运行。这些程序使用**__attribute__((constructor))编写,在 LC_ROUTINES[_64] 中定义(现在已弃用),或者通过指针在一个带有 S_MOD_INIT_FUNC_POINTERS 标志的部分中(通常为:__DATA.__MOD_INIT_FUNC**)。
终结器使用**__attribute__((destructor))编写,并位于一个带有 S_MOD_TERM_FUNC_POINTERS 标志的部分中(__DATA.__mod_term_func**)。
存根
macOS 中的所有二进制文件都是动态链接的。因此,它们包含一些存根部分,帮助二进制文件在不同的机器和上下文中跳转到正确的代码。在执行二进制文件时,需要 dyld 来解析这些地址(至少是非懒加载的地址)。
二进制文件中的一些存根部分:
__TEXT.__[auth_]stubs:来自__DATA部分的指针__TEXT.__stub_helper:调用带有要调用函数信息的动态链接的小代码__DATA.__[auth_]got:全局偏移表(指向导入函数的地址,在解析后绑定(在加载时绑定,因为它标记为S_NON_LAZY_SYMBOL_POINTERS))__DATA.__nl_symbol_ptr:非懒加载符号指针(在加载时绑定,因为它标记为S_NON_LAZY_SYMBOL_POINTERS)__DATA.__la_symbol_ptr:惰性符号指针(首次访问时绑定)
请注意,带有前缀 "auth_" 的指针使用一个进程内加密密钥进行保护(PAC)。此外,可以使用 arm64 指令 BLRA[A/B] 在跟随指针之前验证指针。而 RETA[A/B] 可以用于替代 RET 地址。
实际上,__TEXT.__auth_stubs 中的代码将使用 braa 而不是 bl 来调用请求的函数以验证指针。
还要注意,当前的 dyld 版本将所有内容加载为非懒加载。
查找惰性符号
有趣的反汇编部分:
可以看到跳转到调用 printf 的位置是在 __TEXT.__stubs:
在**__stubs**部分的反汇编中:
你可以看到我们正在跳转到GOT的地址,在这种情况下,它是通过非延迟解析的,将包含printf函数的地址。
在其他情况下,而不是直接跳转到GOT,它可以跳转到**__DATA.__la_symbol_ptr,它将加载一个代表它正在尝试加载的函数的值,然后跳转到__TEXT.__stub_helper,它跳转到包含dyld_stub_binder地址的__DATA.__nl_symbol_ptr,该函数接受函数编号和地址作为参数。
在找到搜索的函数地址后,该最后一个函数将其写入__TEXT.__stub_helper**中的相应位置,以避免将来进行查找。
但请注意,当前dyld版本将所有内容都作为非延迟加载。
Dyld操作码
最后,**dyld_stub_binder**需要找到指定的函数并将其写入正确的地址,以免再次搜索。为此,它在dyld内部使用操作码(有限状态机)。
apple[]参数向量
在macOS中,主函数实际上接收4个参数而不是3个。第四个称为apple,每个条目的形式为key=value。例如:
结果:
当这些值到达主函数时,敏感信息已经被删除,否则可能会导致数据泄漏。
在进入主函数之前,可以通过调试查看所有这些有趣的值:
dyld_all_image_infos
这是由dyld导出的一个结构,包含有关dyld状态的信息,可以在源代码中找到,包括版本、指向dyld_image_info数组的指针、指向dyld_image_notifier的指针、如果进程与共享缓存分离、是否调用了libSystem初始化程序、指向dyld自身Mach头文件的指针、指向dyld版本字符串的指针...
dyld环境变量
调试dyld
有助于了解dyld操作的有趣环境变量:
DYLD_PRINT_LIBRARIES
检查加载的每个库:
DYLD_PRINT_SEGMENTS
检查每个库是如何加载的:
DYLD_PRINT_INITIALIZERS
打印每个库初始化程序运行时的信息:
其他
DYLD_BIND_AT_LAUNCH: 惰性绑定将使用非惰性绑定解析DYLD_DISABLE_PREFETCH: 禁用对 __DATA 和 __LINKEDIT 内容的预取DYLD_FORCE_FLAT_NAMESPACE: 单级绑定DYLD_[FRAMEWORK/LIBRARY]_PATH | DYLD_FALLBACK_[FRAMEWORK/LIBRARY]_PATH | DYLD_VERSIONED_[FRAMEWORK/LIBRARY]_PATH: 解析路径DYLD_INSERT_LIBRARIES: 加载特定库DYLD_PRINT_TO_FILE: 将 dyld 调试信息写入文件DYLD_PRINT_APIS: 打印 libdyld API 调用DYLD_PRINT_APIS_APP: 打印主程序调用的 libdyld APIDYLD_PRINT_BINDINGS: 绑定时打印符号DYLD_WEAK_BINDINGS: 仅在绑定时打印弱符号DYLD_PRINT_CODE_SIGNATURES: 打印代码签名注册操作DYLD_PRINT_DOFS: 打印加载的 D-Trace 对象格式部分DYLD_PRINT_ENV: 打印 dyld 可见的环境变量DYLD_PRINT_INTERPOSTING: 打印 interposing 操作DYLD_PRINT_LIBRARIES: 打印加载的库DYLD_PRINT_OPTS: 打印加载选项DYLD_REBASING: 打印符号重新定位操作DYLD_RPATHS: 打印 @rpath 的扩展DYLD_PRINT_SEGMENTS: 打印 Mach-O 段的映射DYLD_PRINT_STATISTICS: 打印时间统计信息DYLD_PRINT_STATISTICS_DETAILS: 打印详细的时间统计信息DYLD_PRINT_WARNINGS: 打印警告消息DYLD_SHARED_CACHE_DIR: 用于共享库缓存的路径DYLD_SHARED_REGION: "use", "private", "avoid"DYLD_USE_CLOSURES: 启用闭包
可以通过类似以下方式找到更多内容:
或者从https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz下载dyld项目,并在文件夹内运行:
参考资料
最后更新于