用外部符号替换 MacOS Mach-O 二进制文件中的静态符号

Posted

技术标签:

【中文标题】用外部符号替换 MacOS Mach-O 二进制文件中的静态符号【英文标题】:Replace static symbols in MacOS Mach-O binary with external symbols 【发布时间】:2019-07-04 03:08:49 【问题描述】:

我有以下场景:

一款专有的 MacOS 游戏,将图形框架 MoltenVK 静态链接到其主要的 Mach-O x86-64 二进制文件中。 链接的 MoltenVK 版本太旧了。 我有一个较新版本的 MoltenVK,格式为 .dylib(如果需要,还有较新版本的 SPIRV 编译器、libVulkan 等,也可以是 dylib 格式)。 旧版和新版 MoltenVK 与 ABI 兼容,这意味着导出的符号 名称 和函数签名在旧版和新版 MoltenVK 中应相同。

以及这次进入 MacOS 链接之旅的根本原因:

游戏在我的 macOS 版本(10.15 Catalina Beta 3)上无法正常运行。由于崩溃回溯,我已将问题隔离到 MoltenVK。 我想测试更新 MoltenVK 是否可以解决问题,既可以作为临时解决方法,也可以帮助开发人员隔离问题。

是否可以强制二进制文件使用动态加载的.dylib 中定义的符号版本,而不是二进制文件本身中定义的版本?我想修补我拥有的每个.dylibs 中可用的 所有 符号,因为如果我只修补某些符号而不修补其他符号,它可能会损坏(大概 MoltenVK 仅在每个符号的代码时才有效框架中来自同一版本的 MoltenVK)。

注意:我无法重新编译游戏的主 Mach-O 二进制文件,因为我没有源代码。如果可能的话,我愿意绕过本地系统上的安全保护措施来执行此操作;无论如何,我接受在运行 Beta(非生产)操作系统时做危险事情的风险。

如果答案和 cmets 专注于所提出问题的技术解决方案,我更愿意,但如果需要进一步的理由,我会尝试尽快隔离这个问题,以便给游戏开发者尽可能多的时间在 macOS 10.15 的最终版本之前修复它。如果我保持沉默,问题就有可能不会被发现;然后人们将升级到最终的 macOS 10.15 并注意到游戏无法运行。这对任何人来说都不好玩,因为那样我们要么留在 Mojave 等待游戏开发者更新他们的游戏,要么可能几周或几个月不玩游戏。

【问题讨论】:

【参考方案1】:

静态链接意味着库被有效地嵌入到您的最终可执行二进制文件中。因此,没有简单的技术方法可以挂钩呼叫并将它们重定向到其他地方(例如 DYLD_INTERPOSEDYLD_INSERT_LIBRARIES 允许外部 dylibs)。

修补二进制文件需要遍历游戏进行的每个 MoltenVK 调用,并进行相当繁琐的后期处理。 通过后期处理,我的意思是:

    使用dlopendlsym 串联编写一个dyld 调用。你还是会 需要已在二进制文件中使用的 dlopendlsym 符号(它们是 libSystem aka C std lib,但您仍然需要专用的 dyld 操作码 能够实际使用它们)。最终,您需要将汇编操作码放在二进制文件的某个位置,以使一切正常。这将非常困难。 触发lldb 调试器,准备dlsym 地址以手动调用并为每次调用即时修补二进制文件(您可能需要__TEXT 段中的写入权限才能做到这一点,但这很容易部分)。如果您知道自己在做什么,这可能是最可行的方法。主要缺点是它是不稳定的,如果你破坏了一些东西,你会从头开始。 将LC_LOAD_DYLIB 命令添加到LC_DYLD_INFO_ONLY 引用的二进制和dyld 操作码,这将是超级难

无论如何,你最好的朋友是Hopper dissassembler 和MachOView 来检查二进制文件。

x86(和/或x86-64)汇编的基本知识必须学习。我认为使用原始源代码可能是一种更可行的选择。

【讨论】:

以上是关于用外部符号替换 MacOS Mach-O 二进制文件中的静态符号的主要内容,如果未能解决你的问题,请参考以下文章

如何优化 App 的启动时间

更改 Mach-o 二进制文件中的函数引用

格式无关的二进制对象?

为啥 32 位内核可以运行 64 位二进制文​​件?

iOS系统分析Mach-O二进制文件解析

iOS小技能:分析dyld的信息