The next sections provide an overview of the key security features in the Android operating system.
Security at OS level through Linux kernel
The Android operating system is built on top of the Linux kernel. Over the past few years, Linux has evolved into a secure operating system trusted by many corporations across the world for its security. Today, most of the mission critical systems and servers run on Linux because of its security. By having the Linux kernel at the heart of its platform, Android tries to ensure security at the OS level. Also, Android has built a lot of specific code into Linux to include certain features related to mobile environment. With each Android release the kernel version also has changed. The following table shows Android versions and the corresponding Linux kernel version:
Linux kernel used in various Android versions
The Linux kernel provides Android with the below key security features:
- A user-based permissions model
- Process isolation
- Extensible mechanism for secure IPC
Android implements a permission model for individual apps. Applications must declare which permissions (in the manifest file) they require. When the application is installed, as shown in the following screenshot, Android will present the list to the user so that they can view the list to allow installation or not:
Unlike a desktop environment, this provides an opportunity for the user to know in advance which resources the application is seeking access to. In other words, user permission is a must to access any kind of critical resource on the device. By looking at the requested permission, the user is more aware of the risks involved in installing the application. But most users do not read these and just give away a lot of permissions, exposing the device to malicious activities.
Note
It is not possible to install an Android app with a few or reduced permissions. You can either install the app with all the permissions or decline it.
As mentioned earlier, developers have to mention permissions in a file named AndroidManifest.xml
. For example, if the application needs to access the Internet, the permission INTERNET
is specified using the following code in the AndroidManifest.xml
file:
Android permissions are categorized into four levels which are as follows:
In order to isolate applications from each other, Android takes advantage of the Linux user-based protection model. In Linux systems, each user is assigned a unique user ID (UID) and users are segregated so that one user does not access the data of another user. All resources under a particular user are run with the same privileges. Similarly, each Android application is assigned a UID and is run as a separate process. What this means is that even if an installed application tries to do something malicious, it can do it only within its context and with the permissions it has.
This application sandboxing is done at the kernel level. The security between applications and the system at the process level is ensured through standard Linux facilities such as user and group IDs that are assigned to applications. This is shown in the following screenshot, referenced from http://www.ibm.com/developerworks/library/x-androidsecurity/.
By default, applications cannot read or access the data of other applications and have limited access to the operating system. If application A tries to read application B's data, for example, then the operating system protects against this because application A does not have the appropriate privileges. Since the application sandbox mechanism is implemented at the kernel level, it applies to both native applications and OS applications. Thus the operating system libraries, application framework, application runtime, and all applications run within the Application Sandbox. Bypassing this sandbox mechanism would require compromising the security of Linux kernel.
Starting with Android 4.3, Security-Enhanced Linux (SELinux) is supported by the Android security model. Android security is based on discretionary access control, which means that applications can ask for permissions, and users can grant or deny those permissions. Thus, malware can create havoc on phones by gaining permissions. But SE Android uses mandatory access control (MAC) which ensures that applications work in isolated environments. Hence, even if a user installs a malware app, the malware cannot access the OS and corrupt the device. SELinux is used to enforce MAC over all processes, including the ones running with root privileges.
SELinux operates on the principle of default denial. Anything that is not explicitly allowed is denied. SELinux can operate in one of two global modes:
permissive mode, in which permission denials are logged but not enforced, and
enforcing mode, in which denials are both logged and enforced. As per Google's documentation, in the Android 5.0 Lollipop release, Android moves to full enforcement of SELinux. This builds upon the permissive release of 4.3 and the partial enforcement of 4.4. In short, Android is shifting from enforcement on a limited set of crucial domains (installd
, netd
, vold
and zygote
) to everything (more than 60 domains).
All Android apps need to be digitally signed with a certificate before they can be installed on a device. The main purpose of using certificates is to identify the author of an app. These certificates do not need to be signed by a certificate authority and Android apps often use self-signed certificates. The app developer holds the certificate's private key. Using the same private key, the developer can provide updates to his applications and share data between applications. In debug mode, developers can sign the app with a debug certificate generated by the Android SDK tools. You can run and debug an app signed in debug mode but the app cannot be distributed. To distribute an app, the app needs to be signed with your own certificate. The key store and the private key which are used during this process need to be secured by the developer as they are essential to push updates. The following screenshot shows the key store selection option that is displayed while exporting the application:
Secure interprocess communication
As discussed in the above sections, sandboxing of the apps is achieved by running apps in different processes with different Linux identities. System services run in separate processes and have more privileges. Thus, in order to organize data and signals between these processes, an interprocess communication (IPC) framework is needed. In Android, this is achieved with the use of the
Binder mechanism.
The Binder framework in Android provides the capabilities required to organize all types of communication between various processes. Android application components, such as intents and content providers, are also built on top of this Binder framework. Using this framework, it is possible to perform a variety of actions such as invoking methods on remote objects as if they were local, synchronous and asynchronous method invocation, sending file descriptors across processes, and so on. Let us suppose the application in Process 'A' wants to use certain behavior exposed by a service which runs in Process 'B'. In this case, Process 'A' is the client and Process 'B' is the service. The communication model using Binder is shown in the following diagram:
All communication between the processes using the Binder framework occurs through the /dev/binder
Linux kernel driver. The permissions to this device driver are set to world readable and writable. Hence, any application may write to and read from this device driver. All communications between the client and server happen through proxies on the client side and stubs on the server side. The proxies and the stubs are responsible for sending and receiving the data, and the commands, sent over the Binder driver.
Each service (also called a Binder service) exposed using the Binder mechanism is assigned with a
token. This token is a 32-bit value and is unique across all processes in the system. A client can start interacting with the service after discovering this value. This is possible with the help of Binder's context manager. Basically, the context manager acts as a name service, providing the handle of a service using the name of this service. In order to get this process working, each service must be registered with the context manager. Thus, a client needs to know only the name of a service to communicate. The name is resolved by the context manager and the client receives the token that is later used for communicating with the service. The Binder driver adds the UID and the PID value of the sender process to each transaction. As discussed earlier, each application in the system has its own UID and this value is used to identify the calling party. The receiver of the call may check the obtained values and decide if the transaction should be completed. Thus, the security is enforced, with the Binder token acting as a security token as it is unique across all processes.
Understanding the boot process of an Android device will help us to understand other forensic techniques which involve interacting with the device at various levels. When an Android device is first powered on, there is a sequence of steps that are executed, helping the device to load necessary firmware, OS, application data, and so on into memory. The following information is compiled from the original post published at http://www.androidenea.com/2009/06/android-boot-process-from-power-on.html.
The sequence of steps involved in Android boot process is as follows:
- Boot ROM code execution
- The boot loader
- The Linux kernel
- The init process
- Zygote and Dalvik
- The system server
We will examine each of these steps in detail.
Before the device is powered on, the device CPU will be in a state where no initializations will have taken place. Once the Android device is powered on, execution starts with the boot ROM code. This boot ROM code is specific to the CPU the device is using. As shown in the the following diagram, this phase includes two steps:
- When, boot ROM code is executed, it initializes the device hardware and tries to detect the boot media. Thus, the boot ROM code scans till it finds the boot media. This is similar to the BIOS function in the boot process of a computer.
- Once the boot sequence is established, the initial boot loader is copied to the internal RAM. After this, the execution shifts to the code loaded into the RAM.
The boot loader is a piece of program that is executed before the operating system starts to function. Boot loaders are present in desktop computers, laptops and mobile devices as well. In an Android boot loader, there are two stages—initial program load (IPL) and
second program load (SPL). As shown in the following diagram, this involves three steps explained as follows:
- IPL deals with detecting and setting up external RAM.
- Once external RAM is available, the SPL is copied into the RAM and execution is transferred to it. The SPL is responsible for loading the Android operating system. It also provides access to other boot modes such as fastboot, recovery, and so on. It initiates several hardware components such as console, display, keyboard and file systems, virtual memory, and other features.
- After this, the SPL tries to look for the Linux kernel. It will load this from the boot media and copy it to the RAM. Once the boot loader is done with this process, it transfers the execution to the kernel.
The Linux kernel is the heart of the Android operating system and is responsible for process management, memory management, and enforcing security on the device. After the kernel is loaded, it mounts the root file system (rootfs) and provides access to system and user data, as described in the following steps:
- When the memory management units and caches have been initialized, the system can use virtual memory and launch user space processes.
- The kernel will look in the rootfs for the init process and launch it as the initial user space process.
The init is the very first process that starts and is the root process of all other processes.
- The init process will look for a script named
init.rc
that describes the system services, file system, and any other parameters that need to be set up.- The
init
process can be found at:<android source>/system/core/init
.. - The
init.rc
file can be found in source tree at <android source>/system/core/rootdir/init.rc
.Tip
More details about the Android file hierarchy will be covered in Chapter 3, Understanding Data Storage on Android Devices.
- The
init
process will parse the init.rc
script and launch the system service processes. At this stage, you will see the Android logo on the device screen.
Zygote is one of the first init processes created after the device boots. It initializes the Dalvik virtual machine and tries to create multiple instances to support each android process. As discussed in earlier sections, the Dalvik virtual machine is the virtual machine which executes Android applications written in Java.
Zygote facilitates using a shared code across the VM, thus helping to save the memory and reduce the burden on the system. After this, applications can run by requesting new Dalvik virtual machines that each one runs in. Zygote registers a server socket for zygote connections, and also preloads certain classes and resources. This Zygote loading process has been more clearly explained at http://www.kpbird.com/2012/11/in-depth-android-boot-sequence-process.html. This is also explained as follows:
Load ZygoteInitclass
: This class loads the ZygoteInit
class. Source Code: <Android Source>/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
.registerZygoteSocket()
: This registers a server socket for zygote command connections.preloadClasses()
: This is a simple text file containing the list of classes that need to be preloaded will be executed here. This file can be seen at <Android Source>/frameworks/base
.preloadResources()
: This deals with native themes and layouts. Everything that includes the android.R
file will be loaded using this method.
All the core features of the device such as telephony, network, and other important functions, are started by the system server, as shown in the following diagram:
The following core services are started in this process:
- Start Power Manager
- Create Activity Manager
- Start Telephony Registry
- Start Package Manager
- Set Activity Manager Service as System Process
- Start Context Manager
- Start System Context Providers
- Start Battery Service
- Start Alarm Manager
- Start Sensor Service
- Start Window Manager
- Start Bluetooth Service
- Start Mount Service
The system sends a broadcast action called ACTION_BOOT_COMPLETED
which informs all the dependent processes that the boot process is complete. After this, the device displays the home screen and is ready to interact with the user. The Android system is now fully operational and is ready to interact with the user.
As explained earlier, several manufacturers use the Android operating system on their devices. Most of these device manufacturers customize the OS based on their hardware and other requirements. Hence, when a new version of Android is released, these device manufacturers have to port their custom software and tweaks to the latest version.
Understanding Android architecture and its security model is crucial to having a proper understanding of Android forensics. The inherent security features in Android OS, such as application sandboxing, permission model, and so on, safeguard Android devices from various threats and also act as an obstacle for forensic experts during investigation. Having gained this knowledge of Android internals, we will discuss more about what type of data is stored on the device and how it is stored, in the next chapter.