在运行时为沙盒应用设置DYLD变量



我有一个Cocoa应用程序,它使用了几个系统和第三方框架,其中一些框架派生了XPC服务。我想在那些XPC服务的进程空间中插入一个我自己的库,这样我就可以将信息反馈给我的主应用程序。音频数据在播放时,作为一个具体的例子,通过插入或马赫拦截。

TL;DR:在应用程序启动时,如何以编程方式为我的沙盒应用程序设置DYLD环境变量?

完整版…通过在启动应用程序之前设置一些shell变量(与这个问题所建议的相反),我可以很容易地插入库:

$ export __XPC_DYLD_FORCE_FLAT_NAMESPACE=1 
$ export __XPC_DYLD_INSERT_LIBRARIES=`pwd`/My.app/Contents/Frameworks/XpcMonitor.dylb
$ open My.app

我认为我在这一点上是自由的,"只是"需要设置这些变量,当我的应用程序启动:

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的lenvironment部分。

 <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>

这可以工作,但是将库的绝对路径硬编码到我的应用程序中。因此,如果我的bundle被放置在/Applications以外的任何地方,它将崩溃,并显示"无法定位插入的库"消息。

我尝试将该路径更改为"@executable_path/../Frameworks/XpcMonitor.dylib"。如果我只是将一个库插入到我自己的应用程序中(即使用DYLD_INSERT_LIBRARIES),但无法找到XPC服务的库,那么这是有效的,可能是因为这些服务是具有不同路径的单独可执行文件。我尝试使用"@rpath/XpcMonitor。并将我的应用程序的搜索路径设置为"@executable_path/../Frameworks",但也未能找到该库。这里可能有什么神奇的东西,但我还没能弄清楚。

我尝试设置变量,然后重新启动我的应用程序:

 [NSTask launchedTaskWithLaunchPath:[[NSBundle mainBundle] executablePath] 
                          arguments:[NSArray array]];

在沙箱外工作得很好,在沙箱内使用:deny forbidden-sandbox-reinit就失败了。

我尝试用-[NSWorkspace launchApplicationAtURL:options:configuration:error]重新启动应用程序,使用NSWorkspaceLaunchNewInstance和NSWorkspaceLaunchAsync标志,并在配置字典中传递环境变量。这在沙箱外工作得很好,但当沙箱时,配置字典的内容被忽略。

我尝试了几种设置变量的变化,然后用[NSTask launchedTaskWithLaunchPath:arguments]启动子进程,包括一个嵌套的应用程序包(在框架,MacOS,助手,插件中尝试过),以及第二个可执行文件,有或没有嵌入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更新失败的火花的预安装代码签名检查。Spctl check out:

 $ spctl --verbose=4 --assess --type execute ~/Desktop/MyApp.app/
 /Users/Jason/Desktop/MyApp.app/: accepted
 source=Developer ID

这似乎仍然是最有可能成功的方法,但我被如何设置代码签名难住了,无论是对于可以访问主应用程序的资源和框架的辅助可执行文件(因为整个UI都在辅助程序中),还是作为子进程运行的嵌套应用程序包。谷歌搜索上面的错误没有出现任何有用的东西,我可以找到,所有关于代码签名助手可执行文件的信息,除了上面链接的那个,都是登录项。

(顺便说一下,我不能为此使用XPC服务,因为我需要很大一部分UI (web视图等)在设置这些变量的情况下运行,而你不能在XPC服务中做UI)。

我想就这些了,如果还需要更多的信息,请告诉我。那么,再一次说明:在应用程序启动时,如何以编程方式为我的沙盒应用程序设置DYLD环境变量?(或者根本不可能,在这种情况下,我可以问心无愧地把这个放在一边,不再困惑?)

好的。你有几处不准确:

首先,在运行时为沙盒应用程序设置DYLD变量的答案是正确的:DYLD将限制自己处理变量有三个原因,其中一个是应用程序是用权利签署的代码(其他两个是restrictedBySetGUid,或restrictedBySegment)。这些东西之所以对你有效,是因为当时你可能没有对应用程序代码进行签名。

第二,__XPC_*环境变量只是普通环境变量的包装,libxpc。在_xpc_collect_environment中使用的Dylib:具体来说,检查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传递(并启动)。当服务被生成时,__XPC_前缀被剥离,以便再次拥有DYLD_*..然后你又回到了起点——如果服务被签名(即,带有权利,因此受到限制),那么dyld将忽略,这可以通过你所说的忽略字典来证实(非dyld变量不受影响)。

并且,当您在启动后设置DYLD变量时,它们不再有任何影响。它们只在Mach-O负载上由DYLD处理。停止令人费解。如果您确实找到了一种方法,您将偶然发现一个0天漏洞(因为特别是DYLD_INSERT_LIBRARIES,是所有邪恶的r00t ..)

最新更新