android虚拟手机云之三:文件沙盒

时间:2021-09-01 19:04:11

总贴 在这里。

支持文件系统隔离

我们管这个叫做文件沙盒。其实android的沙盒是不彻底的,因为sdcard下的路径全部APP共享。 到了android 8,App就算不用外部sdcard也可以放一堆东西,通过FileProvider也可以提供跨应用的数据。但是正如大家诟病的,许多APP还是会申请sdcard的读写权限。
我们在测试的过程中,发现某些安全库,会写死一些边边角角的可读写的地方,去放东西,这个很恶心。

哦,说句题外话,android系统的东西都是放在/data/data下的,许多奇奇的目录都是符号连接,rm -rf /data/data/* 就可以达到出厂设置的效果。

ok,我们的方案是怎么做的呢,有几个点:

  • 要根据上文的instance_no,来新建文件目录
  • 在zygote初始化的时候动态mount新建的目录,设置这个目录可读写
  • 在kernel中,如果是多开进程的文件访问,都给截获,重新映射到这个mount的目录

instance_no部分上一篇博客说了,mount部分是这样做的:
在zygote的 pid_t pid = fork() pid != 0之后

if (access (abs_path, F_OK) < 0) {
    ALOGW ("Zg: Target path %s for %s does not exist: err=%d, skip it.", abs_path, source_path, errno);
    continue;
}

if (TEMP_FAILURE_RETRY (mount (source_path, abs_path, NULL, MS_BIND, NULL)) < 0) {
    ALOGE ("Zg: Failed to mount %s to %s: err=%d", source_path, abs_path, errno);
    closedir (app_root);
    RuntimeAbort (env);
}

值得一提的是,zygote fork进程之后,通过设置

SetCapabilities(env, permittedCapabilities, effectiveCapabilities);

把linux上的权限给阉割了,这也是为什么su 要在另外一个进程执行的原因(因为zygote 建出来的进程就没有任何权限)

kernel部分,主要是修改syscall部分。就是把open、access等系统调调的时候,获取标识位,看看是不是hook过的,如果是的话,就映射下读取路径。这样的话,/data/data 下的文件路径就被映射到我们上面mount的路径中去了。

kernel/fs/open.c

#ifdef CONFIG_INTERCEPT_FILE_SYSCALLS
    /* Lionfore for ML. Sep 11th, 2017 */
    if (this_process_should_be_intercepted ()) {
        xd_access_syscall_req_t req;
        xd_access_syscall_res_t rsp;

        res = __filename_absolute_path (dfd, filename, lookup_flags, req.path);
        if (res < 0)
            goto __trace_syscall; /* Lionfore for ML. Sep 15th, 2017 */

        req.hdr.type = XD_ACCESS_SYSCALL_REQ;
        req.hdr.len = sizeof req;
        req.hdr.pid = current->pid;
        req.hdr.app_tag = get_process_inst_no (current);
        req.mode = mode;
        req.uid = __kuid_val (current_cred ()->euid);
        req.gid = __kgid_val (current_cred ()->egid);
        rsp.hdr.len = sizeof rsp;
        res = intercept_syscall (&req.hdr, &rsp.hdr);
        if (res < 0)
            goto __trace_syscall; /* Lionfore for ML. Sep 15th, 2017 */

        if (rsp.hdr.err < 0) {
            res = rsp.hdr.err;
            goto __trace_syscall; /* Lionfore for ML. Sep 15th, 2017 */
        }
    }
#endif

需要注意的是 readlink 这个系统调用也要改,代码在

kernel/fs/namei.c

关于软连接和硬连接的介绍,看这里

小结一下。上面介绍了通过zygotekernel的系统调用的修改。在打开新应用的时候动态创建目录,动态映射目录,做到了文件系统隔离的效果。

其实在android8之后,规范的写android,机会可以不用用到外部存储,所以应用里面的简单沙盒其实是够用的。

不够用的时候,可能是出于 唯一标识 && 安全 方面的考量。

  • 比如我在每个碎片话的系统(mi,huawei、魅族)在一些边边角角可读可写的目录,用户不会清理到的地方,放一点自定义文件,存着key,然后全家桶都用这个key来作为用户标识。
  • 如果在两个不同的账号下同时读到同一个key,那就是多开了:)