Mac沙盒:运行需要/tmp的二进制工具

9
我有一个沙盒化的Cocoa应用,在导出过程中需要运行第三方命令行工具。该工具似乎将其临时文件硬编码为使用/tmp,而沙盒不允许访问此文件夹,因此导出失败。
我该如何使该工具运行?由于我无法访问其源代码,因此无法修改它以使用NSTemporaryDirectory(),并且它似乎不遵守TMPTEMPDIR环境变量。出于我不理解的原因,给自己授予com.apple.security.temporary-exception.files.absolute-path.read-write权限也似乎不起作用。
是否有一些方法可以在我的沙盒内重新映射文件夹?是否有某种鲜为人知的技巧可供使用?我应该尝试修补该工具的二进制文件吗?我已经束手无策了。

2
我脑海中立刻浮现出的一个解决方案是使用 DYLD_INSERT_LIBRARIES,如此处所述,并覆盖 open/fopen 和其他相关系统调用。 - user3159253
此外,您可能需要使用 dtrace 工具来检查第三方工具需要覆盖哪些系统调用。dtruss 似乎是 DTrace MacOSX 内核设施的一个命令行前端。 - user3159253
你能控制工具如何访问/tmp吗?如果你可以用自定义脚本替换它的调用,那么你就有了解决方案。如果不能,那么需要一些额外的技巧。 - user3159253
@user3159253,悬赏将在不到一天后到期。如果您想要领取,请写点什么! - Becca Royal-Gordon
哦,不好意思,请原谅!这些天我几乎没有上线,所以当我看到你成功解决了问题时,我忘记领取奖品了!谢谢! - user3159253
显示剩余2条评论
3个回答

6
我成功使用了user3159253的DYLD_INSERT_LIBRARIES方法。我希望他们能写一篇描述如何实现此方法的答案,因此我将留下那些细节并解释与此案例特定相关的部分。
通过LLDB、努力和Hopper的帮助,我发现第三方工具使用mkstemp()生成其临时文件名,并且一些调用(不是全部)使用以/tmp开头的固定模板。然后,我编写了一个名为libtmphack.dylib的拦截对mkstemp()调用并在调用标准库版本之前修改参数的程序。
由于mkstemp()接受一个指向预分配缓冲区的指针,因此我觉得我不能将以类似“/tmp”这样的短字符串开头的路径重写为需要进入沙箱内部的Caches文件夹的非常长的字符串。相反,我选择在当前工作目录中创建一个名为“$tmp”的符号链接。如果该工具在不合适的时间调用chdir(),则可能会出现问题,但幸运的是它似乎没有这样做。

这是我的代码:

//
//  libtmphack.c
//  Typesetter
//
//  Created by Brent Royal-Gordon on 8/27/14.
//  Copyright (c) 2014 Groundbreaking Software. This file is MIT licensed.
//

#include "libtmphack.h"
#include <dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
//#include <errno.h>
#include <string.h>

static int gbs_has_prefix(char * needle, char * haystack) {
    return strncmp(needle, haystack, strlen(needle)) == 0;
}

int mkstemp(char *template) {
    static int (*original_mkstemp)(char * template) = NULL;

    if(!original_mkstemp) {
        original_mkstemp = dlsym(RTLD_NEXT, "mkstemp");
    }

    if(gbs_has_prefix("/tmp", template)) {
        printf("libtmphack: rewrote mkstemp(\"%s\") ", template);
        template[0] = '$';
        printf("to mkstemp(\"%s\")\n", template);

        // If this isn't successful, we'll presume it's because it's already been made
        symlink(getenv("TEMP"), "$tmp");

        int ret = original_mkstemp(template);

        // Can't do this, the caller needs to be able to open the file
        // int retErrno = errno;
        // unlink("$tmp");
        // errno = retErrno;

        return ret;
    }
    else {
        printf("libtmphack: OK with mkstemp(\"%s\")\n", template);
        return original_mkstemp(template);
    }
}

非常快速而粗糙,但它的效果很好。

快速而优雅,来自一个更文明的时代。 - Prof. Falken
@Prof.Falken 我已经写了七年的Objective-C;在那之前,我在一个大型开源项目(一个解释器)中使用C编写了相当广泛的库和抽象层;在那之前,我学习了C++的计算机科学。但我认为这是我第一次用纯粹的C编写代码,没有任何花哨的库来帮助我。 - Becca Royal-Gordon
哈哈,有了那样的背景,我可以理解为什么普通的类Unix API看起来很快而且很粗糙。 :) - Prof. Falken

3
另一种解决方案是在子进程(或posix_spawn选项)中使用chroot,将其根目录更改为位于您沙盒内的目录。然后它的“/tmp”将成为该目录中的“tmp”目录。

3

由于@BrentRoyal-Gordon已经发布了一个可行的解决方案,我只是复制了我的评论,激发了他产生了解决方案:

为了修复程序的行为,我将通过DYLD_INSERT_LIBRARIES和自定义共享库中给定系统调用的自定义实现来拦截和覆盖一些系统调用。

需要被覆盖的系统调用的确切列表取决于应用程序的性质,并且可以使用建立在MacOS DTrace内核设施上的许多工具进行研究。例如dtrussHopper。 @BrentRoyal-Gordon已经确定该应用程序仅需要一个/适当的/ mkstemp 实现即可解决问题。

就是这样。我仍然不确定我是否值得获得奖金 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接