我有一个Cocoa应用程序,它使用多个系统和第三方框架,其中一些框架剥离了XPC服务。我想将自己的库插入到这些XPC服务的进程空间中,以便我可以将信息反馈给我的主应用程序。正在播放的音频数据,作为一个具体的例子,通过插入或马赫拦截。
TL; DR:当我的应用程序启动时,如何以编程方式为我的沙盒应用程序设置DYLD环境变量?
更长的版本......
我可以通过在启动我的应用程序之前设置一些shell变量来轻松地插入库(与this question建议的相反):
$ export __XPC_DYLD_FORCE_FLAT_NAMESPACE=1
$ export __XPC_DYLD_INSERT_LIBRARIES=`pwd`/My.app/Contents/Frameworks/XpcMonitor.dylb
$ open My.app
我认为此时我在家是免费的,而且#34;只是"在我的应用程序启动时需要设置这些变量:
int main(int argc, const char * argv[])
{
NSString* libPath = [[[NSBundle mainBundle] privateFrameworksPath] stringByAppendingPathComponent:@"XpcMonitor.dylib"];
setenv("__XPC_FORCE_FLAT_NAMESPACE", "1", 1);
setenv("__XPC_DYLD_INSERT_LIBRARIES", [libPath UTF8String], 1);
return NSApplicationMain(argc, argv);
}
但这没有效果。这看起来很奇怪,因为据我所知,XPC流程还没有出现。显然,Launch Services已经决定了它如何在到达main()之前处理子进程。为了确保它没有在库构造函数中发生,我将所有应用程序逻辑移动到dylib,并在设置变量后动态加载它;相同的结果。
我尝试将它们添加到Info.plist的LSEnvironment部分。
<key>LSEnvironment</key>
<dict>
<key>__XPC_DYLD_FORCE_FLAT_NAMESPACE</key>
<string>1</string>
<key>__XPC_DYLD_INSERT_LIBRARIES</key>
<string>/Applications/My.app/Contents/Frameworks/XpcMonitor.dylib</string>
</dict>
这样可行,但硬编码库的绝对路径到我的应用程序中。因此,如果我的软件包被放置在/ Applications之外的任何位置,它将崩溃,并且#34;无法找到插入的库&#34;消息。
我尝试将该路径更改为&#34; @executable_path /../ Frameworks / XpcMonitor.dylib&#34;。如果我只是将库插入我自己的应用程序(即使用DYLD_INSERT_LIBRARIES),但无法找到XPC服务的库,大概是因为这些服务是具有不同路径的单独可执行文件。我尝试使用&#34; @ rpath / XpcMonitor.dylib&#34;并将我的应用程序的搜索路径设置为&#34; @executable_path /../ Frameworks&#34;但那也找不到图书馆。这里可能有一些魔力可能,但我还没能弄明白。
我尝试设置变量,然后重新启动我的应用程序:
[NSTask launchedTaskWithLaunchPath:[[NSBundle mainBundle] executablePath]
arguments:[NSArray array]];
在沙箱之外工作得很好,在其内部失败:拒绝禁止沙箱重新启动。
我尝试使用NSWorkspaceLaunchNewInstance和NSWorkspaceLaunchAsync标志重新启动应用程序 - [NSWorkspace launchApplicationAtURL:options:configuration:error],并在配置字典中传递环境变量。这在沙盒之外很有效,但是当沙盒化时,配置字典的内容将被忽略。
我尝试了几种变体来设置变量然后使用[NSTask launchTaskWithLaunchPath:arguments]启动子进程,包括嵌套的应用程序包(在Frameworks,MacOS,Helpers,PlugIns中尝试)和第二个可执行文件有和没有embedded Info.plist。所有这些变化都会运行并发挥作用,但会引发一些令人担忧的沙盒错误,其中包括许多错误:
5/30/14 9:55:04.000 AM kernel[0]: Sandbox: appleeventsd(57) deny file-read-metadata /Library
其中一些:
5/30/14 9:55:04.000 AM kernel[0]: Sandbox: appleeventsd(57) deny mach-lookup com.apple.ocspd
最后这个:
appleeventsd[57]: <rdar://problem/11489077> A sandboxed application with pid 2119, "MyAppHelper" checked in with appleeventsd, but its code signature could not be validated ( either because it was corrupt, or could not be read by appleeventsd ) and so it cannot receive AppleEvents targeted by name, bundle id, or signature. Error=ERROR: #-67061 { "NSDescription"="SecCodeCheckValidity() returned -67061, <SecCode 0x7fa30bc0bd20 [0x7fff7b46ff00]>." } (handleMessage()/appleEventsD.cp #2072) client-reqs-q
...此应用程序的Sparkle更新失败了Sparkle的安装前代码签名检查。 Spctl检查出来:
$ spctl --verbose=4 --assess --type execute ~/Desktop/MyApp.app/
/Users/Jason/Desktop/MyApp.app/: accepted
source=Developer ID
这似乎仍然是最有可能获得成功的方法,但是我对如何设置代码签名感到困惑,无论是对于可以访问主应用程序资源和框架的帮助程序可执行文件(因为整个UI在帮助程序中),或作为子进程运行的嵌套应用程序包。谷歌搜索上面的错误并没有找到我能找到的任何有用信息,除了上面链接的代码签名助手可执行文件之外的所有信息都是针对登录项目的。
(顺便说一下,我不能使用XPC服务,因为我需要使用这些变量集来运行UI的大部分(Web视图等),你可以&#39 ;在XPC服务中执行UI。)
哇...我认为这就是一切,但如果需要更多信息,请告诉我。再一次:当我的应用程序启动时,如何以编程方式为我的沙盒应用程序设置DYLD环境变量? (或者根本不可能,在这种情况下,我可以用清醒的良心把它放在一边并停止令人费解?)
答案 0 :(得分:2)
确定。你有几件事情不准确:
首先,Setting DYLD variables for sandboxed app at runtime答案是正确的:由于三个原因,DYLD将自己限制处理变量,其中一个原因是应用程序是使用权利签署的代码(另外两个是restrictedBySetGUid或restrictedBySegment) 。事情对您有用的原因是因为当时您可能没有签署您的应用代码。
其次,__ XPC_ *环境变量只是普通变量的包装,libxpc.dylib在_xpc_collect_environment中使用它:具体来说,检查10.10.3的代码,你会看到:
__xpc_collect_environment:
0000000000004970 pushq %rbp
....
00000000000049f7 leaq 0x1daa1(%rip), %rsi ## literal pool for: "__XPC_"
00000000000049fe callq __xpc_has_prefix
在生成xpc服务之前,变量通过XPC(和launchd)传递。当生成服务时,__XPC_前缀被剥离,以便再次具有DYLD_ * ..并且您回到原点 - 如果服务已签名(即具有权限,因此受限制),dyld将忽略你的说法会证明字典被忽略了(非DYLD变量不受影响)。
而且,当您在启动后设置DYLD变量时,它们不再有任何效果。它们仅在Mach-O负载上由DYLD处理。别疑惑了。如果你找到了办法,你将偶然发现一个0天的漏洞(因为DYLD_INSERT_LIBRARIES,尤其是所有邪恶的r00t ......)