Android - Arquitectura

Todas las distribuciones de Android siguen la misma arquitectura (para mantener la compatibilidad de aplicaciones).

Arquitectura Android
Copyright; Android Security Internals - Nikolay Elenkov
  1. Bootloader

    Los dispositivos ARM tienen una característica; dependen de una implementación externa para tener un bootloader funcional.

    De esta manera nos podemos encontrar diferentes bootloaders dependiendo del fabricante, cada uno tiene su propia implementación. Sumale que al ser propietarios, la mayoría son de código cerrado, sin documentaciones y de extrema dificultad para acceder (es parte del firmware embebido).

    El bootloader es el encargado de (en este orden); ver e inicializar la memoria, verificar que los certificados del sistema operativo Android sean del fabricante (que está intrinsicamente relacionado al uso de UEFI y el bloqueo OEM), verificar las particiones “boot”, “dtbo”, “init_boot” y “recovery”, verificar si existen actualizaciones aplicar en el sistema o si al fallar el sistema (o si el usuario mantiene los botones correctos) debe iniciar el modo recovery.

    Por último, carga las imágenes de inicio; “boot.img”, “vendor_boot.img”, “init_boot.img” y otras imágenes propietarias del fabricante. Al igual que que u-boot, grub, lilo y demás cargadores carga el kernel y el “initramfs” en memoria.

    A partir de Android 12 el bootloader soporta pasar configuraciones al kernel para modificar el inicio por defecto.

    En el archivo; system/core/mkbootimg/bootimg.h está la configuración de inicio.

    En sistemas Android hablamos del modo “fasboot” cuando podemos interactuar con el bootloader. Sus estados son “LOCKED” ó “UNLOCKED”, dependiendo uno u otro va impedir la instalación de otro SO o no (respectivamente). Por defecto en encuentra en modo “LOCKED”, permitiendo cargar una distribución Android firmada digitalmente por el fabricante. Ese certificado digital es lo que se conoce como “Root of Trust”, y en el dispositivo solamente se encuentra la parte pública de esa llave, el fabricante se queda con la privada. Algunos dispositivos permiten que el usuario indique su propia llave, pero no es la norma.

  2. Kernel

    Utilizan el kernel Linux con algunas modificaciones para alivianar el mismo.

    Android posee de forma nativa habilitado SELinux (Security Enhanced Linux), entre otros componentes a nivel de kernel.

    Internamente el kernel utiliza el IPC (Inter Process Comunication) para permitir que distintos programas puedan comunicarse entre si de una forma controlada y segura. Esto es así por que un programa al ejecutarse se encuentra aislado en la memoria RAM, a nivel de hardware directamente, a través de las tablas de traducción. En Android el sistema de IPC se llama; Binder, y funciona como un módulo (‘driver’ ó ‘kernel object’) del kernel. Es similar a Windows Common Object Model (COM) y el Common Object Broker Request Architectures (CORBA) de Unix, pero pero, a diferencia de éstas, se ejecuta en un único dispositivo y no admite llamadas a procedimientos remotos (RPC) a través de la red. llamadas a procedimientos remotos (RPC) a través de la red (aunque podría sobre Binder).

    El módulo tiene su interfaz expuesta en; “/dev/binder” y utiliza llamadas “ioctl” con la estructura “binder_write_read”. Cuando el programa “A” quiere hacer uso del IPC, se conecta a la interfaz expuesta para enviar el mensaje, luego binder (hasta ahora solo lectura) de conecta a su propio módulo para pasar a modo escritura y alocar una nueva porción de memoria en el programa “B” (hecho por el kernel, su memory management y las Virtual Addresses - VA - ) que al finalizar su lectura es liberado de nuevo.

    Binder
    Copyright; Android Security Internals - Nikolay Elenkov

    Hay abstracciones del IPC como; Intents, Menssenjers y ContentProviders.

  3. SysInit

    A diferencia del system init de la mayoría de las distribuciones Linux; SystemD u OpenRC, el de Android es muy simple y tiene modificaciones específicas para funcionar con la máquina virtual de Java y las aplicaciones instaladas.

    Si analizamos una aplicación, podemos identificar si se va a iniciar con el sistema operativo cuando encontramos esta capacidad (‘capability’);

    • android.permission.RECEIVE_BOOT_COMPLETED

    Esto se encuentra especificado en el archivo “AndroidManifiest.xml” dentro del propio apk.

  4. Android ART / Dalvik

    Tanto Dalvik como ART (Android Runtime) son entornos de ejecución (véase, presenta y contiene todo para que aplicaciones se puedan ejecutar apropiadamente arriba) que se utilizan para hacer andar las aplicaciones en el sistema operativo.

    Desde hace un tiempo ART ya remplaza a Dalvik.

    Como ART y Dalvik usan Java para funcionar, ocurria un problema; como Java (no confundir con JavaScript, son dos cosas diferentes) es un lenguaje interpretado, cada vez que la aplicación se iniciaba había que esperar a que la aplicación se compile para luego poder ejecutarla (lo que se conoce como “JIT”: “just-in-time”) cuando se usaba Dalvik. Esto no es así con el uso de ART, en donde se usa un esquema de “AOT”; “ahead-of-time” que compila la aplicación apenas es instalada en el dispositivo.

    Así mismo ART trae mejoras en rendimiento, el recolector de basura (característica de los lenguajes interpretados), depuración de la aplicación, etc. Y empezó a estar disponible a partir de Android 4.4.

    Dalvik usa archivos “.dex” cuando la aplicación es compilada e iniciada (“DEX”; “Dalvik EXecutable”). El archivo APK contiene un solo archivo “dex” llamado “classes.dex”, por temas de compatibilidad esto se mantiene en ART.

    Dalvik usa archivos “.odex” cuando cachea (véase; procesar y almacenar cosas que no cambian) la aplicación, para hacer más rápido su inicio. Muchas veces esto también se usaba para segmentar la aplicación hasta tal punto que sí o sí parte de la misma estaba solamente en el archivo “.odex”, haciendo tremendamente dificil su ingenieria inversa ya que se tenía que realizar el proceso inverso y empaquetar todo correctamente dentro del apk. Esto no se mantiene en ART, si no que utiliza el formato ejecutable nativo del kernel Linux; “ELF”: “Executable and Linkable Format” el cual permite una mayor optimización en muchos sentidos (la librería estándar, las dependencias externas, el “LTO”: “Link Time Optimization”, etc) y un menor consumo de recursos en todo sentido.

    Copyright; Wikipedia

    Si hasta ahora vas entendiendo te darás cuenta de una cosa si queres realizar ingeniería inversa a un programa; como desde Android 4.4 KitKat se usa principalmente ART vas a tener que urgar en el apk los archivos “dex” y tener conocimiento de Java o Kotlin para poder entender que hace la aplicación leyendo el código.

  5. Servicios de sistema

    Los servicios de sistema son programas expuestos a través del IPC (Inter Process Comunication) para permitir que distintas aplicaciones puedan realizar acciones determinadas en el sistema operativo, a fin de que no se encuentren aisladas totalmente si no que puedan (por ejemplo); verificar si uno está a través de 3G/4G/5G o WiFi, solicitar la validación de identidad a través de la huella digital o reconocimiento facial, etc.

    La mayoría, por no decir todos, están programados en Kotlin, algunas ‘legacy’ están en Java puro y unas pocas en código nativo (C, C++, Rust, etc).

    El punto de exposición de un servicio/programa para disposición del resto se llama; ‘interfaz’. La interfaz de la JVM (‘Java Virtual Machine’) que controla los servicios del sistema operativo como tal se especifica cuando en el código se usa;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;