Android security defense (III): seandroid zygote
In the Android system, all application processes and system service process systemserver are fork bred by zygote. The native acquisition of zygote mainly studies the access permissions of Dalvik/Vm/native/dalvik_system_zygote.cpp and seandroid control application resources. The entire Dalvik is also operated here.
First, let's take a look at the thrown dalviknativemethod dvm_dalvik_system_zygote. Compared with native Android, seandroid adds two string-type parameters in nativeforkandspecialize:
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 },}
So what are the two parameters? Continue with 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);}
We can see that two parameters are added: seinfo, which is used to define the seandroid information of the new process and nicename, which is used to define the new process name.
In static pid_t forkandspecializecommon (const U4 * ARGs, bool issystemserver), seandroid adds the setting SELinux security context code segment, seinfo and 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
The security context method of SELinux is set as follows:
#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
Next, the libcore/Dalvik/src/main/Java/Dalvik/system/zygote. Java and zygote classes are encapsulated. The seinfo and nicename parameters are added to the forkandspecialize method.
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); }...}
The Startup Process of the android application is not described in detail. After the zygoteconnection object is set up for socket connection, the next step is to call the zygoteconnection. runonce function for further processing.
Source code location: Frameworks/base/CORE/Java/COM/Android/Internal/OS/zygoteconnection. java. Specifically, seandroid adds the zygote security policy function and calls it in 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; }
Naturally, when starting a new process, seandroid information seinfo will also be added to frameworks/base/CORE/Java/Android/OS/process. java.