本文由 简悦 SimpRead转码, 原文地址 developer.apple.com
本帖是与可信执行系统相关的帖子集的一部分。如果您发现自己的 w......
本帖是与可信执行系统相关的帖子集的一部分。如果您是直接找到这里的,我建议您 从顶部开始。
Resolving Library Loading Problems
在 macOS 上,动态链接器负责加载进程使用的动态链接库。这分为两个部分:
-
当进程运行可执行文件时,动态链接器会加载该可执行文件使用的所有库,以及这些库使用的库,等等。
-
进程可以使用
dlopen和NSBundle等应用程序接口在运行时加载库。有关dlopen及其朋友的信息,请参阅dlopenman page。
动态链接器与可信执行系统密切合作,确保只加载适当的库。动态链接库冒充攻击是一个重要问题。如果你的程序引用了某个库,你希望动态链接器加载的是该库的副本,而不是攻击者安装的其他副本。
主要的保护措施是库验证。如果在可执行文件上启用了库验证,则可信执行系统只允许进程加载经 Apple 签名或与可执行文件具有相同团队 ID 的代码。
库验证由 Hardened Runtime 启用,但您可以使用 Disable Library Validation Entitlement (com.apple.security.cs.disable-library-validation)权限退出库验证。
重要 请启用库验证。只有当您的应用程序需要加载其他第三方开发人员提供的插件时,才能禁用它。禁用库验证会增加通过 Gatekeeper 的难度。有关详情,请参阅 解决加载命令路径混乱导致的 Gatekeeper 问题。
当动态链接器加载库失败时,它会在崩溃报告中给出解释。例如
Termination Reason: Namespace DYLD, Code 1 Library missing
Library not loaded: @rpath/libEtranger.dylib
Referenced from: /Users/USER/*/LinkToEtranger.app/Contents/MacOS/LinkToEtranger
Reason: …
(terminated at launch; ignore backtrace)
Application Specific Information:
Library not loaded: @rpath/libEtranger.dylib
Referenced from: …
Reason: …
这一解释可能会被崩溃报告系统截断。要查看完整日志,请从终端运行应用程序:
% ./LinkToEtranger.app/Contents/MacOS/LinkToEtranger
dyld[79650]: Library not loaded: @rpath/libEtranger.dylib
Referenced from: …/LinkToEtranger.app/Contents/MacOS/LinkToEtranger
Reason: tried: '…/LinkToEtranger.app/Contents/MacOS/../Frameworks/libEtr
anger.dylib' (code signature in <E16EDD14-CE5A-33BC-9B06-554A3BC12C51>
'…/LinkToEtranger.app/Contents/Frameworks/libEtranger.dylib' not valid
for use in process: mapping process and mapped file (non-platform) have
different Team IDs), '…/LinkToEtranger.app/Contents/MacOS/../
Frameworks/libEtranger.dylib' (code signature in <E16EDD14-CE5A-33BC-
9B06-554A3BC12C51> '…/LinkToEtranger.app/Contents/Frameworks/
libEtranger.dylib' not valid for use in process: mapping process and
mapped file (non-platform) have different Team IDs), '/usr/local/lib/
libEtranger.dylib' (no such file), '/usr/lib/libEtranger.dylib' (no
such file)
zsh: abort ./LinkToEtranger.app/Contents/MacOS/LinkToEtranger
"Reason "行超长,请尝试将其分开:
'…/LinkToEtranger.app/Contents/MacOS/../Frameworks/libEtranger.dylib'
(…),
'/usr/local/lib/libEtranger.dylib' (no such file),
'/usr/lib/libEtranger.dylib' (no such file)
每个条目都以动态链接器试图查找库的位置开头,然后在括号内加上文本,如 "无此类文件",解释出错的原因。
注意 这些信息的确切格式因 macOS 的不同版本而异。
其中许多原因与可信执行系统无关。例如,"无此类文件 "表示磁盘上没有该库。不过,有三个常见的可信执行问题:
-
库验证
-
使用旧的 macOS SDK
-
库代码的权限受限
有关动态链接器的更多信息,请参阅 dyld man page。具体来说,"DYLD_PRINT_SEARCHING "环境变量在调试库加载问题时非常有用。
Library Validation
在任何实际情况下,动态链接器的 Reason 输出都超长。为了更好地理解它,请尝试将其拆分:
'…/LinkToEtranger.app/Contents/MacOS/../Frameworks/libEtranger.dylib'
(code signature in <E16EDD14-CE5A-33BC-9B06-554A3BC12C51>
'…/LinkToEtranger.app/Contents/Frameworks/libEtranger.dylib'
not valid for use in process: mapping process and mapped file
(non-platform) have different Team IDs),
'/usr/local/lib/libEtranger.dylib' (no such file),
'/usr/lib/libEtranger.dylib' (no such file)
动态链接器查找了三个不同的地方:
-
应用程序的 "Frameworks "目录
-
/usr/local/lib目录 -
/usr/lib目录
第一个地方很重要,因为它的路径与预期的库位置一致。动态链接器记录了对问题的精彩解释:
code signature in … '…/LinkToEtranger.app/Contents/Frameworks/
libEtranger.dylib' not valid for use in process: mapping process
and mapped file (non-platform) have different Team IDs
总之,动态链接器没有加载这个libEtranger.dylib副本,因为它不是系统库(非平台),而且它的 Team ID 与进程的主可执行文件不同。到 codesign 快速查看一下就能证实这一点:
% codesign -d -vvv LinkToEtranger.app
…
TeamIdentifier=SKMME9E2Y8
…
% codesign -d -vvv LinkToEtranger.app/Contents/Frameworks/libEtranger.dylib
…
TeamIdentifier=VL9SQP756U
…
如何解决这个问题取决于产品的性质。如果该库是作为产品的一部分安装的,请使用与您的团队 ID 相关联的签名标识重新签署该库。即使您没有亲自构建代码,也要这样做。毕竟,你负责将库安装到用户的机器上,其签名应反映这一点。
还有一种可能是,你正在构建一个支持插件的程序,因此需要加载由其他第三方开发者签名的插件。在这种情况下,解决方法是使用 Disable Library Validation Entitlement 权限 (com.apple.security.cs.disable-library-validation)签署可执行文件,从而禁用库验证。
重要 禁用库验证会增加通过 Gatekeeper 的难度。有关详情,请参阅 解决由悬垂加载命令路径引起的 Gatekeeper 问题。
Use of an Old macOS SDK
另一个与可信执行系统有关的动态库加载故障如下所示:
code signature in … '…/LinkToDodo.app/Contents/Frameworks/libDodo.dylib'
not valid for use in process: mapped file has no cdhash, completely
unsigned? Code has to be at least ad-hoc signed.
注意 此信息中的 "cdhash "指的是代码目录哈希值。有关 cdhashes 的更多信息,请参阅 TN3126 Inside Code Signing: Hashes。
这就更难理解了,尤其是因为该库实际上 是 签名的:
% codesign -d -vvv LinkToDodo.app/Contents/Frameworks/libDodo.dylib
…
Authority=Apple Development: …
…
在 Notarizing macOS Software Before Distribution中可以找到解释:
苹果的公证服务要求你采取以下保护措施:
...
- 与 macOS 10.9 或更高版本的 SDK 相链接
macOS 10.9 引入了重要的代码签名改进。加固运行时依赖于这些改进。它会通过查看代码所使用的 SDK 来确认这些改进是否存在。如果代码是使用旧版 SDK 构建的,或没有使用 SDK 的记录,加固运行时就会拒绝加载代码。
在本例中,LinkToDodo 应用程序与现代 SDK 进行了链接,但libDodo.dylib中却没有与之构建的 SDK 的记录:
% vtool -show-build LinkToDodo.app/Contents/MacOS/LinkToDodo
…
cmd LC_BUILD_VERSION
…
sdk 12.3
…
% vtool -show-build LinkToDodo.app/Contents/Frameworks/libDodo.dylib
LinkToDodo.app/Contents/Frameworks/libDodo.dylib:
%
这就是错误的原因:
-
进程已启用加固运行时。
-
加固运行时要求所有代码都使用 macOS 10.9 SDK 或更高版本构建。
-
libDodo.dylib没有使用 SDK 构建的记录,因此可信执行系统阻止加载它。 -
动态链接器在解释问题时会报告这一点。
最好的解决办法是使用最新工具从源代码中重建代码。如果你现在无法这样做,请参阅 Notarisation and the macOS 10.9 SDK 了解解决方法。
重要 这是一项短期兼容性措施。计划尽快从源代码中重建此代码。如果你从其他第三方开发者那里获得了代码,请确保他们知道这个问题。
最后,如果您只能在现场重现此问题,并设法获取了系统诊断日志,请在系统日志中查找类似以下的日志条目:
type: default
time: 2022-05-20 13:12:11.185889 +0100
process: kernel
category: <Missing Description>
message: …/LinkToDodo.app/Contents/Frameworks/libDodo.dylib: Possible race detected. Rejecting.
这是一把隐秘的 "烟枪"!
有关系统日志的一般信息,请参阅 Your Friend the System Log。
第三个与可信执行系统有关的动态链接库加载失败情况如下:
…
OS Version: macOS 11.6.5 (20G527)
…
Termination Reason: DYLD, [0x5] Code Signature
Application Specific Information:
dyld: launch, loading dependent libraries
Dyld Error Message:
Library not loaded: @rpath/OverlyEntitled.framework/Versions/A/OverlyEntitled
Referenced from: /Users/USER/AppWithEntitlementLibrary.app/Contents/MacOS/AppWithEntitlementLibrary
Reason: no suitable image found. Did find:
…/AppWithEntitlementLibrary.app/Contents/MacOS/../Frameworks/OverlyEntitled.framework/Versions/A/OverlyEntitled: code signature invalid for '…/AppWithEntitlementLibrary.app/Contents/MacOS/../Frameworks/OverlyEntitled.framework/Versions/A/OverlyEntitled'
…
注意 本崩溃报告来自 macOS 11。由于......嗯......原因......macOS 12 忽略了库代码的权限。不过,这在 macOS 13 中又发生了变化,其失败方式与 macOS 11 大同小异。
不过,代码签名是有效的:
% codesign -v -vvv AppWithEntitlementLibrary.app/Contents/Frameworks/OverlyEntitled.framework
AppWithEntitlementLibrary.app/Contents/Frameworks/OverlyEntitled.framework: valid on disk
AppWithEntitlementLibrary.app/Contents/Frameworks/OverlyEntitled.framework: satisfies its Designated Requirement
它还通过了上一节概述的两项测试:
% codesign -d -vvv AppWithEntitlementLibrary.app
…
TeamIdentifier=SKMME9E2Y8
…
% codesign -d -vvv AppWithEntitlementLibrary.app/Contents/Frameworks/OverlyEntitled.framework
…
TeamIdentifier=SKMME9E2Y8
…
% vtool -show-build AppWithEntitlementLibrary.app/Contents/Frameworks/OverlyEntitled.framework/Versions/A/OverlyEntitled
…
sdk 12.3
…
问题在于框架签署了受限权限:
% codesign -d --entitlements - AppWithEntitlementLibrary.app/Contents/Frameworks/OverlyEntitled.framework
…
[Dict]
[Key] com.apple.developer.networking.vpn.api
[Value]
[Array]
[String] allow-vpn
权限只有应用于主可执行文件时才有效。非主可执行文件的代码称为 库代码 ,包括框架、动态库和捆绑包。不要对库代码应用权限。充其量这只是善意的。更糟的是,它会导致代码签名崩溃,就像下面这样。
注 有关主可执行文件的详细信息,请参阅为 Mac 创建发行版签名代码
TN3125 Inside Code Signing: Provisioning Profiles 中的 Entitlements on macOS 部分定义了受限权限,并明确指出在 macOS 上,可执行文件要求的每个受限权限都必须获得其供应配置文件的授权。不过,库代码没有嵌入式供应配置文件:
-
共享库没有捆绑结构,因此 不能 包含供应配置文件。
-
有捆绑结构的库代码、框架和捆绑包 可以 有供应配置文件,但包括 Xcode 在内的大多数工具都不嵌入供应配置文件。
因此,OverlyEntitled 框架声称拥有受限权限,但该声称并未获得配置文件的授权,因此可信执行系统会告诉动态链接器不要加载它。
要解决这个问题,可以更改代码签名设置,使只有主可执行文件才可申请权限。有关该主题的详细建议,请参阅 Creating Distribution-Signed Code for Mac。
重要 导致此问题的首要原因是用户使用
--deep签署代码。出于这个原因以及--deep被认为有害中概述的其他原因,请勿这样做。
2022-12-13 更新了 Restricted Entitlements on Library Code 部分的说明,以考虑 macOS 13 "回到度量标准"。
2022-09-26 修复了一个断开的链接。
2022-06-13 添加了 "库代码中的受限权限 "部分。
2022-05-20 首次发布。