嵌入应用的Launch Daemon无法启动:启动约束违规排查
我有一个macOS应用,其中嵌入了一个暴露XPC服务的launch daemon。该launch daemon已通过ServiceManagement API成功向launchd注册:
% launchctl print system/com.nsnolan.LockerRoomDaemon system/com.nsnolan.LockerRoomDaemon = { active count = 0 path = (submitted by smd.303) type = Submitted state = spawn scheduled program identifier = Contents/MacOS/LockerRoomDaemon (mode: 2) parent bundle identifier = com.nsnolan.LockerRoom parent bundle version = 1 BTM uuid = <redacted> default environment = { PATH => /usr/bin:/bin:/usr/sbin:/sbin } environment = { XPC_SERVICE_NAME => com.nsnolan.LockerRoomDaemon } domain = system minimum runtime = 10 exit timeout = 5 runs = 83 last exit code = 78: EX_CONFIG endpoints = { "com.nsnolan.LockerRoomDaemon" = { port = 0xfaaab active = 0 managed = 1 reset = 0 hide = 0 watching = 0 } } spawn type = interactive (4) jetsam priority = 40 jetsam memory limit (active) = (unlimited) jetsam memory limit (inactive) = (unlimited) jetsamproperties category = daemon submitted job. ignore execute allowed jetsam thread limit = 32 cpumon = default job state = spawn failed probabilistic guard malloc policy = { activation rate = 1/1000 sample rate = 1/0 } properties = partial import | supports transactions | resolve program | needs LWCR update | has LWCR }
我使用同一开发者证书(带有相同的团队标识符)为应用和launch daemon签名:
% codesign -d --verbose=4 /User/USER/Library/Developer/Xcode/DerivedData/LockerRoom-eyzvsjkjmatcbndmhghzrtcwfqqf/Build/Products/Debug/LockerRoom.app Executable=/User/USER/Library/Developer/Xcode/DerivedData/LockerRoom-eyzvsjkjmatcbndmhghzrtcwfqqf/Build/Products/Debug/LockerRoom.app/Contents/MacOS/LockerRoom Identifier=com.nsnolan.LockerRoom Format=app bundle with Mach-O thin (arm64) CodeDirectory v=20400 size=50938 flags=0x0(none) hashes=1581+7 location=embedded VersionPlatform=1 VersionMin=918016 VersionSDK=918528 Hash type=sha256 size=32 CandidateCDHash sha256=<redacted> CandidateCDHashFull sha256=<redacted> Hash choices=sha256 CMSDigest=<redacted> CMSDigestType=2 Executable Segment base=0 Executable Segment limit=2703360 Executable Segment flags=0x1 Page size=4096 CDHash=<redacted> Signature size=4792 Authority=Apple Development: yeahrightgetlost@urayahoo.com (<redacted>) Authority=Apple Worldwide Developer Relations Certification Authority Authority=Apple Root CA Signed Time=Jul 27, 2024 at 22:35:20 Info.plist entries=24 TeamIdentifier=<redacted> Sealed Resources version=2 rules=13 files=4 Internal requirements count=1 size=196 codesign -d --verbose=4 /User/USER/Library/Developer/Xcode/DerivedData/LockerRoom-eyzvsjkjmatcbndmhghzrtcwfqqf/Build/Products/Debug/LockerRoom.app/Contents/MacOS/LockerRoomDaemon Executable=/User/USER/Library/Developer/Xcode/DerivedData/LockerRoom-eyzvsjkjmatcbndmhghzrtcwfqqf/Build/Products/Debug/LockerRoom.app/Contents/MacOS/LockerRoomDaemon Identifier= com.nsnolan.LockerRoomDaemon Format=Mach-O thin (arm64) CodeDirectory v=20400 size=8353 flags=0x0(none) hashes=250+7 location=embedded VersionPlatform=1 VersionMin=918016 VersionSDK=918528 Hash type=sha256 size=32 CandidateCDHash sha256=<redacted> CandidateCDHashFull sha256=<redacted> Hash choices=sha256 CMSDigest=<redacted> CMSDigestType=2 Executable Segment base=0 Executable Segment limit=409600 Executable Segment flags=0x1 Page size=4096 CDHash=<redacted> Signature size=4792 Authority=Apple Development: yeahrightgetlost@urayahoo.com (<redacted>) Authority=Apple Worldwide Developer Relations Certification Authority Authority=Apple Root CA Signed Time=Jul 27, 2024 at 22:35:18 Info.plist=not bound TeamIdentifier=<redacted> Sealed Resources=none Internal requirements count=1 size=204
该launch daemon未配置RunAtLoad。我尝试从应用发送XPC消息以启动daemon时,观察到以下日志信息:
AMFI: Launch Constraint Violation (enforcing), error info: c[5]p[1]m[1]e[0], (Constraint not matched) launching proc[vc: 3 pid: 1948]: /User/USER/Library/Developer/Xcode/DerivedData/LockerRoom-eyzvsjkjmatcbndmhghzrtcwfqqf/Build/Products/Debug/LockerRoom.app/Contents/MacOS/LockerRoomDaemon, launch type 0, failure proc [vc: 3 pid: 1948]: /User/USER/Library/Developer/Xcode/DerivedData/LockerRoom-eyzvsjkjmatcbndmhghzrtcwfqqf/Build/Products/Debug/LockerRoom.app/Contents/MacOS/LockerRoomDaemon ASP: Security policy would not allow process: 1948, /User/USER/Library/Developer/Xcode/DerivedData/LockerRoom-eyzvsjkjmatcbndmhghzrtcwfqqf/Build/Products/Debug/LockerRoom.app/Contents/MacOS/LockerRoomDaemon
同时出现如下崩溃报告:
Incident Identifier: 47BFEBAE-8BE8-443C-8705-EC7FB7269879 CrashReporter Key: E7C56EE2-21DE-F362-2A8E-B971E1F74B69 Hardware Model: MacBookPro18,3 Process: LockerRoomDaemon [1761] Path: /Users/USER/Library/Developer/Xcode/DerivedData/LockerRoom-eyzvsjkjmatcbndmhghzrtcwfqqf/Build/Products/Debug/LockerRoom.app/Contents/MacOS/LockerRoomDaemon Identifier: LockerRoomDaemon Version: ??? Code Type: ARM-64 (Native) Role: Default Parent Process: launchd [1] Coalition: com.nsnolan.LockerRoomDaemon [1209] Date/Time: 2024-07-27 23:01:47.0847 -0500 Launch Time: 2024-07-27 23:01:46.9770 -0500 OS Version: macOS 14.2 (23C64) Release Type: User Report Version: 104 Exception Type: EXC_CRASH (SIGKILL (Code Signature Invalid)) Exception Codes: 0x0000000000000000, 0x0000000000000000 Termination Reason: CODESIGNING 4 Launch Constraint Violation
当我禁用SIP并使用临时签名时,daemon可正常启动并处理XPC消息。是什么原因导致AMFI检测到启动约束违规?
我认为macOS应用不需要配置描述文件,且Xcode界面似乎也阻止我配置。我还尝试过让daemon使用与应用相同的标识符签名(通过Xcode的“其他代码签名标志”设置),但问题仍未解决。
从日志和签名信息来看,导致AMFI启动约束违规的核心原因主要有以下几点:
1. Daemon标识符存在无效空格
查看daemon的签名输出,Identifier= com.nsnolan.LockerRoomDaemon里标识符前多了一个空格,这会导致AMFI验证时,签名的标识符与注册的服务标识符com.nsnolan.LockerRoomDaemon不匹配,直接触发约束违规。
2. Daemon未绑定Info.plist且缺少必要权限声明
system域的launch daemon必须绑定自己的Info.plist,用来声明服务标识、版本等核心信息,但你的daemon签名显示Info.plist=not bound,同时也没有启用Hardened Runtime权限:
- SIP启用时,system级服务要求必须开启Hardened Runtime,Debug环境下需勾选
Debugging Tools权限,否则AMFI会拦截启动 - 父App需要拥有
com.apple.security.sm.system-service权限,才能注册system域的daemon
3. System域服务的启动约束限制
通过ServiceManagement API注册的system域daemon,在SIP启用状态下有严格的验证逻辑:
- 只能由已签名且符合Hardened Runtime要求的父App触发启动
- daemon的签名必须与父App使用同一团队证书,且权限配置完整
具体解决步骤:
- 修正标识符空格问题:在Xcode的daemon目标中,检查
Bundle Identifier字段,确保没有多余空格,重新构建签名后,用codesign -d -i /path/to/daemon验证标识符完全匹配com.nsnolan.LockerRoomDaemon - 为daemon添加并绑定Info.plist:给daemon目标创建Info.plist,填写
CFBundleIdentifier、CFBundleVersion等必填字段,在Xcode的Build Settings中指定Info.plist File的正确路径,确保签名时绑定该文件 - 启用Hardened Runtime并配置权限:给父App和daemon都开启Hardened Runtime,Debug环境下勾选
Debugging Tools权限;父App需添加com.apple.security.sm.system-service权限,允许其注册system级服务 - 验证签名一致性:确保daemon和父App使用同一团队的开发者证书签名,daemon的签名需包含XPC通信必要的权限(如使用应用组则添加
com.apple.security.application-groups)
内容的提问来源于stack exchange,提问作者NSNolan

