lunes, 12 de febrero de 2024

Instalación de binarios y creación de un entorno chroot Debian en Android


 

Una vez ganemos el root en nuestro móvil estaremos limitados por los ejecutables que este contenga de fábrica. Para poder realizar más acciones necesitaremos instalar binarios ejecutables adicionales que nos permitirán hacerle todo tipo de cosas al hardware.

¿Cómo funcionan los binarios en Android?

Android contiene binarios ejecutables nativos que provienen de diferentes fuentes. Los binarios comunes que encuentras en dispositivos Android comerciales pueden ser parte de toolbox o toybox, y algunos también pueden provenir del código de AOSP.

Los binarios se encuentran en las rutas /system/bin, /system/xbin, y /sbin:

  • /system/bin: En este directorio se encuentran los binarios esenciales que son parte de AOSP y son comunes a la mayoría de los dispositivos Android. Estos ejecutables proporcionan funciones básicas del sistema y herramientas de línea de comandos.

  • /system/xbin: Se usa para almacenar binarios adicionales que pueden no ser esenciales para las funciones básicas del sistema. En este directorio se colocan los binarios adicionales que hemos compilado por nuestra cuenta.

  • /sbin: Este directorio se utiliza para almacenar binarios críticos del sistema que están relacionados con operaciones del sistema a nivel más bajo. A menudo contiene binarios que son necesarios para el arranque del sistema y otras operaciones esenciales. En Android, este directorio podría contener binarios específicos del fabricante o personalizaciones del sistema.

Para construir bibliotecas nativas (archivos .so) y binarios ejecutables específicos que puedan ser usados por las aplicaciones de Android o en una consola, como adb shell, Google proporciona el Kit de Desarrollo Nativo (NDK). Esta herramienta es especialmente útil cuando se requiere un rendimiento optimizado y se elige escribir ciertas partes de la aplicación en lenguajes de bajo nivel. Sin embargo el hecho de que un binario ejecutable haya sido optimizado dentro del NDK no significa que tenga todos los permisos necesarios para funcionar en Android. Necesitarán el acceso root los binarios que realicen tareas como: acceder y modificar las particiones del sistema, acceso a algunos archivos y directorios como archivos de configuración críticos y directorios sensibles, modificar o controlar procesos y servicios del sistema operativo, acceso a ciertos componentes de hardware, como el controlador de la CPU, la interfaz de red avanzada, o ajustes específicos de hardware. No debemos confundir el NDK, destinado para construir binarios y bibliotecas con el Software Development Kit (SDK), destinado para el desarrollo de aplicaciones .apk.

Instalar binarios sin ser root es complicado por la limitación de ejecutar chmod para otorgar permisos de ejecución o el impedimento de ejecutar chown, ya que el sistema de archivos puede estar montado con restricciones de seguridad que impiden cambios en los propietarios de ciertos archivos o directorios, como describíamos en el anterior artículo sobre cómo conseguir el root. Se podría intentar instalar en la ruta /data/local/tmp/ ya que esta ruta se utiliza para almacenar y realizar ciertas operaciones temporales. Por lo general, la instalación de binarios requiere de root.

Para instalar binarios estáticos que son aquellos que no dependen de ninguna biblioteca, por lo que se pueden usar directamente, una forma sencilla es compilarlos para el hardware de nuestro móvil (arm ó arm64) y copiarlos a la carpeta destino con adb push /ruta/de/nuestro/pc/origen /ruta/destino/móvil.

¿Cómo instalar busybox estático, busybox dinámico y/u otros binarios?

Para ganar algunas nuevas funcionalidades podemos instalar los binarios individualmente o hacer una instalación de “busybox” que es una especie de navaja suiza que alberga muchos binarios en un mismo ejecutable. El repertorio de instrucciones que contiene es mayor que el de toolbox o toybox, que son sus alternativas. Existen dos tipos de compilaciones de busybox, la estática que se vale por sí sola y ocupa más espacio, o la dinámica que depende de bibliotecas (.so) que pueden ser compartidas por otras aplicaciones y ocupa menos espacio. En ambas instalaciones se usan enlaces simbólicos que ejecutan el binario busybox. Las bibliotecas se suelen almacenar en /system/lib. Para instalar cualquier binario simplemente basta con haberlo compilado para la arquitectura en la que estamos trabajando y copiarlo a su carpeta de destino.

Instalando busybox ganaremos algunas funcionalidades pero aún nos faltarán otras para poder sacarle todo el provecho al hardware. Otra forma de actuar sería instalar un sistema operativo Linux con todo los binarios necesarios para explorar todas las capacidades de nuestro móvil. El camino más largo es adaptar una distribución Linux a nuestro hardware o buscar si alguien ya ha implementado una solución más rápida para nuestro propósito. Las instrucciones más sencillas para probar un sistema Linux, como podría ser Debian, son instalar el sistema de archivos en una partición del móvil, como podría ser el almacenamiento externo y entrar en modo fastboot para indicarle al móvil que arranque desde el kernel indicado. Estas instrucciones se pueden encontrar aquí.

En vez de portar un nuevo sistema operativo a nuestro hardware, podemos aprovechar las propiedades del sistema operativo que ya tenemos, Android, e implementar los recursos que necesitemos. Esto se puede conseguir con un entorno “chroot” y de esta forma nos evitaremos el tedioso trabajo de adaptar todos los controladores y el software del sistema operativo al hardware, aún coste en ciertas restricciones al hardware que se podrían superar implementando modificaciones en el sistema operativo principal.

Si creamos un entorno “chroot” sin “root” muchas de las operaciones necesarias como la instalación de software y la manipulación de ciertos directorios del sistema estarán restringidas y puede que no funcionen, por lo que para un mayor beneficio necesitaremos el “root” del teléfono.

El comando chroot de Linux cambia el directorio raíz para un proceso y sus hijos. De esta forma se consigue un entorno aislado dentro del sistema de archivos principal, limitando el acceso del proceso a un subdirectorio específico. Chroot sólo proporciona aislamiento a nivel de sistema de archivos. No aísla completamente otros recursos como procesos, red o memoria. Dentro de este entorno, no se tendrá acceso a recursos fuera de este directorio por defecto. Por lo que para poder hacer uso de otros directorios hay que montarlos con mount y la opción -o rw (permiso de lectura-escritura) o vincularlo mediante -o bind. (Recuerda desmontar en orden inverso a como has montado, para evitar dependencias entre ficheros).

El entorno chroot más básico que podemos construir es una jaula con busybox. Para ello simplemente ejecutamos:

chroot /mnt/busybox /bin/busybox --install

chroot /mnt/Debian /bin/env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin /bin/sh

Ó

chroot /mnt/Debian /bin/sh

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin (Definiendo la variable PATH)


Construyendo Debian dentro de Android

Hay diferentes formas de construir un sistema Debian en un entorno chroot desde Android. Una de ellas es desde una imagen mínima y luego completar la instalación. En la dirección https://ftp.debian.org/debian/dists/stable/main/installer-arm64/current/images/ tenemos las opciones “cdrom/”, “device-tree/”, “netboot/” y “u-boot/”. La imagen “cdrom/” está destinada para instalaciones desde CD-ROM. La carpeta “device-tree/” contiene archivos .dtb que son archivos utilizados para describir la información de hardware específica de una plataforma. Esto ficheros se usan en sistemas integrados como la Raspberry pi. La carpeta “u-boot/” contiene imágenes destinada a dispositivos que funcionan con un gestor de arranque U-Boot.

La carpeta “netboot/” contiene imágenes destinadas a instalaciones mínimas cómo la que hemos comentado.

Otro tipo de instalación podría ser usar una imagen cloud. En el caso de Debian se podría descargar desde https://cloud.debian.org/images/cloud/ e instalarla.

Existen otras distribuciones que ya viene preparadas para entornos chroot como es el caso de AlpineOS. En este caso solo tenemos que descomprimir el fichero y configurar el sistema.

Pero la forma más eficiente para instalar Debian en un entorno chroot, es mediante debootstrap, una herramienta basada en un script que automatiza la construcción del sistema de archivos de un sistema Debian en un directorio específico de nuestro PC. Para ello necesitamos un PC con una distribución de Linux en el que ejecutaremos “debootstrap”.

Al correr “debootstrap” con “--foreign” estaremos indicando que vamos a realizar una instalación en dos pasos, completándose en un dispositivo distinto, y se le agregamos el parámetro “--arch=arm64” le indicamos que el dispositivo de destino tiene una arquitectura ARM de 64 bits. Esto hace que se genere también una carpeta con todos los binarios necesarios para hacer uso de debootstrap en esa arquitectura. Lo ideal sería partir de una jaula chroot con busybox dentro de Android y poder ejecutar “debootstrap” directamente en el mismo dispositivo, pero para ello tendríamos que resolver todas sus dependencias y modificar el script.

Lo ejecutamos en nuestro PC Linux:

sudo debootstrap --foreign --arch=arm64 stable /home/pc/DebianARM64 http://ftp.debian.org/debian

Comprimimos el directorio Debian:

sudo tar -cf DebianARM64.tar ./DebianARM64/

Se lo pasamos al móvil por adb:

adb push ./DebianARM64.tar /storage/self/primary/

Accedemos a una shell del móvil:

adb shell

Ya en el móvil, descomprimimos y montamos:

tar -xf /storage/self/primary/DebianARM64.tar -C /mnt/

mount -t proc none /mnt/DebianARM64/proc

mount -t sysfs none /mnt/DebianARM64/sys

mount -o bind /dev /mnt/DebianARM64/dev

chroot DebianARM64/ /bin/bash

Definimos variables:

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Ejecutamos la segunda etapa de la instalación, que consiste en la configuración del sistema:

cd /debootstrap

./debootstrap --second-stage

Cuando termine, configuramos el password del usuario root, y añadimos el usuario con el que vamos a trabajar:

passwd root

adduser androidDebian

Salimos y volvemos a entrar logueandonos con:

chroot /mnt/DebianARM64 /bin/login

Una vez logueados tenemos que añadir los usuarios “aid_*” que utiliza Android para poder hacer uso del hardware. Podemos encontrar una lista completa de los grupos y usuarios de Android en su código fuente https://android.googlesource.com/platform/system/core/+/refs/tags/android-8.1.0_r50/libcutils/include/private/android_filesystem_config.h

groupadd -g 3001 aid_net_bt_admin

groupadd -g 3002 aid_net_bt

groupadd -g 3003 aid_inet

groupadd -g 3004 aid_net_raw

groupadd -g 3005 aid_net_admin

groupadd -g 3006 aid_net_bw_stats

groupadd -g 3007 aid_net_bw_acct

groupadd -g 3008 aid_net_bt_stack

Añadimos a los grupos los usuarios “root” y los que hallamos creado, en mi caso “android

usermod -G 3003 -a root

usermod -G 3003 -a android

Para que el gestor de paquetes “apt” tenga acceso a internet hay que introducirlo en los grupos secundarios aid_inet y aid_net_raw. El grupo principal puede ser cambiado por aid_inet.

usermod -g 3003 -G 3003,3004 -a _apt

Cuando dejemos de usar el sistema Linux tenemos que desmontar los discos en sentido inverso al que hemos montado:

umount -o bind /dev /mnt/DebianARM64/dev

umount -t sysfs none /mnt/DebianARM64/sys

umount -t proc none /mnt/DebianARM64/proc

Para guardar nuestro sistema operativo Linux y continuar otro día con el, podemos comprimirlo en un .tar o crear una imagen de disco.

Si decidimos crear una imagen, creamos el fichero:

touch /mnt/DebianARM64.img

Lo llenamos de zeros:

dd if=/dev/zero of=/mnt/DebianARM64.img bs=1M count=2048

Le damos formato ext4:

mke2fs -t ext4 /mnt/DebianARM64.img

Lo adjudicamos al loop0:

losetup /dev/block/loop0 ./DebianARM64.img

Lo montamos:

mkdir /mnt/imagen

mount -o rw -t ext4 /dev/block/loop0 /mnt/imagen

Copiamos la carpeta raíz:

mv -r /mnt/DebianARM64 /mnt/imagen

O si decidimos comprimirlo en un fichero .tar:

tar -cf Debian.tar /mnt/DebianARM64

Aplicaciones y proyectos

Todo este proceso puede automatizarse por medio de una aplicación para Android. Así es como trabaja LinuxDeploy, una app que inicializa un entorno chroot con busybox e instala una distribución Linux con una versión adaptada del script debootstrap. LinuxDeploy permite la instalación de Alpine, Arch, CentOS, Debian, Fedora, Kali, Slackware y Ubuntu en un entorno chroot dentro de Android, creando para ello, según deseemos, una imagen de disco, un directorio en una tarjeta flash, una partición o directamente sobre la RAM. La distribución Linux instalada es accesible desde SSH y VNC para su uso a distancia, de esta forma puedes convertir tu móvil en un PC temporalmente. No es funcional al 100% por que el Hardware está ocupado por los servicios de Android y su kernel. Todos los cambios realizados en el dispositivo son reversibles, es decir, la aplicación y los componentes se pueden eliminar por completo.

Un proyecto pensando para convertir tu móvil Android en un PC completamente funcional es maruos.com. Este proyecto esta pensado para hacer un uso dual del hardware. Para ello reemplaza el sistema operativo Android por el firmware Maru, el cuál permite tener en el dispositivo móvil una distribución adaptada de LineageOS y al conectarlo por HDMI a una pantalla tendríamos Maru Desktop, una versión adaptada de Debian 9, comportándose como si fuera un PC. Permite conectar teclado y ratón por bluetooth.

Desde Termux también se puede realizar una instalación de Debian con GUI en un entorno chroot. Además se puede lograr sin ser root al ejecutar la instrucción proot, una implementación de chroot en el espacio de usuario, lo que significa que no necesita ningún privilegio.

Una solución para instalar un sistema Linux completo con su kernel es hacer uso de Qemu o KVM. Podríamos hacer uso de Qemu en dispositivos que no cuenten con emulación por hardware, corriendo toda la emulación por software, lo que podría suponer en un alto consumo de recursos. Y hacer uso de KVM en dispositivos que tengan disponible la emulación por hardware.