`

Android 安全攻防(三): SEAndroid Zygote

 
阅读更多

Android 安全攻防(三): SEAndroid Zygote


在Android系统中,所有的应用程序进程,以及系统服务进程SystemServer都是由Zygote孕育fork出来的。 Zygote的native获取主要研究dalvik/vm/native/dalvik_system_Zygote.cpp,SEAndroid管控应用程序资源存取权限,对于整个dalvik,也正是在此动的手脚。


首先看抛出的DalvikNativeMethoddvm_dalvik_system_Zygote,与原生Android相比,SEAndroid 在nativeForkAndSpecialize 增加传入了两个String类型的参数:

const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {
    {"nativeFork", "()I",
        Dalvik_dalvik_system_Zygote_fork },
    { "nativeForkAndSpecialize", "(II[II[[ILjava/lang/String;Ljava/lang/String;)I",
        Dalvik_dalvik_system_Zygote_forkAndSpecialize },
    { "nativeForkSystemServer", "(II[II[[IJJ)I",
        Dalvik_dalvik_system_Zygote_forkSystemServer },
    { "nativeExecShell", "(Ljava/lang/String;)V",
        Dalvik_dalvik_system_Zygote_execShell },
    { NULL, NULL, NULL },
}

那么这两个参数是什么呢?继续追一下forkAndSpecialize。

/* native public static int forkAndSpecialize(int uid, int gid,
* int[] gids, int debugFlags, String seInfo, String niceName);
*/
static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args,
JValue* pResult)
{
    pid_t pid;

    pid = forkAndSpecializeCommon(args, false);

    RETURN_INT(pid);
}

可以看到,增加传入的2个参数一个是seInfo,用于定义新进程的SEAndroid信息,一个是niceName,用于定义新进程名。

在static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)中,其中SEAndroid加入了设置SELinux安全上下文代码段,seInfo和niceName:

#ifdef HAVE_SELINUX
    err = setSELinuxContext(uid, isSystemServer, seInfo, niceName);
    if (err < 0) {
        LOGE("cannot set SELinux context: %s\n", strerror(errno));
        dvmAbort();
    }
    free(seInfo);
    free(niceName);
#endif

其中设置SELinux安全上下文方法实现:

#ifdef HAVE_SELINUX
/*
* Set SELinux security context.
*
* Returns 0 on success, -1 on failure.
*/
static int setSELinuxContext(uid_t uid, bool isSystemServer,
const char *seInfo, const char *niceName)
{
#ifdef HAVE_ANDROID_OS
    return selinux_android_setcontext(uid, isSystemServer, seInfo, niceName);
#else
    return 0;
#endif
}
#endif


再往上一层就到了libcore/dalvik/src/main/java/dalvik/system/Zygote.java ,Zygote类的封装,对应forkAndSpecialize方法中添加seInfo和niceName参数传递。

public class Zygote {
...
    public static int forkAndSpecialize(int uid, int gid, int[] gids,
            int debugFlags, int[][] rlimits, String seInfo, String niceName) {
        preFork();
        int pid = nativeForkAndSpecialize(uid, gid, gids, debugFlags, rlimits, seInfo, niceName);
        postFork();
        return pid;
    }


    native public static int nativeForkAndSpecialize(int uid, int gid,
            int[] gids, int debugFlags, int[][] rlimits, String seInfo, String niceName);


    /**
     * Forks a new VM instance.
     * @deprecated use {@link Zygote#forkAndSpecialize(int, int, int[], int, int[][])}
     */
    @Deprecated
    public static int forkAndSpecialize(int uid, int gid, int[] gids,
            boolean enableDebugger, int[][] rlimits) {
        int debugFlags = enableDebugger ? DEBUG_ENABLE_DEBUGGER : 0;
        return forkAndSpecialize(uid, gid, gids, debugFlags, rlimits, null, null);
    }
...
}


Android应用程序启动流程不再赘述,当建立了ZygoteConnection对象用于socket连接后,接下来就是调用ZygoteConnection.runOnce函数进一步处理了。

源码位置:frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java,其中,SEAndroid增加zygote安全策略函数,在runOnce中调用。

 /**
     * Applies zygote security policy.
     * Based on the credentials of the process issuing a zygote command:
     * <ol>
     * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
     * wrapper command.
     * <li> Any other uid may not specify any invoke-with argument.
     * </ul>
     *
     * @param args non-null; zygote spawner arguments
     * @param peer non-null; peer credentials
     * @throws ZygoteSecurityException
     */
    private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer,
            String peerSecurityContext)
            throws ZygoteSecurityException {
        int peerUid = peer.getUid();

        if (args.invokeWith != null && peerUid != 0) {
            throw new ZygoteSecurityException("Peer is not permitted to specify "
                    + "an explicit invoke-with wrapper command");
        }

        if (args.invokeWith != null) {
            boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
                                                         peerSecurityContext,
                                                         "zygote",
                                                         "specifyinvokewith");
            if (!allowed) {
                throw new ZygoteSecurityException("Peer is not permitted to specify "
                    + "an explicit invoke-with wrapper command");
            }
        }
    }

    /**
     * Applies zygote security policy for SEAndroid information.
     *
     * @param args non-null; zygote spawner arguments
     * @param peer non-null; peer credentials
     * @throws ZygoteSecurityException
     */
    private static void applyseInfoSecurityPolicy(
            Arguments args, Credentials peer, String peerSecurityContext)
            throws ZygoteSecurityException {
        int peerUid = peer.getUid();

        if (args.seInfo == null) {
            // nothing to check
            return;
        }

        if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) {
            // All peers with UID other than root or SYSTEM_UID
            throw new ZygoteSecurityException(
                    "This UID may not specify SEAndroid info.");
        }

        boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
                                                     peerSecurityContext,
                                                     "zygote",
                                                     "specifyseinfo");
        if (!allowed) {
            throw new ZygoteSecurityException(
                    "Peer may not specify SEAndroid info");
        }

        return;
    }


理所当然的,在启动一个新的进程时,frameworks/base/core/java/android/os/Process.java中也会加入SEAndroid信息seInfo。






分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics