LatchHook: Hack para poner un Latch en apps de Android (y de paso, aprender cómo funciona por dentro)

jueves, 5 de marzo de 2015

Existen diferentes escenarios en los que podría ser interesante poner un Latch a determinadas acciones en un dispositivo Android: controlar el envío de SMS; bloquear llamadas a números de teléfono de tarificación especial; limitar la instalación, desinstalación y ejecución de aplicaciones en un esquema de supervisión como parte de un MDM o control parental...

Pensando en este último caso y como prueba de concepto, hemos escrito LatchHook, un hack para Android utilizando la librería Substrate (utilizada tanto en la cocina de ROMs como para ayudar en el análisis dinámico de aplicaciones) que utilizando Latch nos permitirá bloquear la ejecución de aplicaciones en un dispositivo A desde un dispositivo B. Si bien se trata de una prueba de concepto, el objetivo a partes iguales es aplicar Latch y conocer más a fondo el sistema de control de Android.

Todo el código está disponible en este proyecto de Github. Se puede adaptar al gusto de quien lo consuma: https://github.com/nodoraiz/latchHooks

El punto de partida

Para poner en marcha el hack necesitaremos pues dos dispositivos para los dos roles: el que será observado (A) y el supervisor (B); además de una cuenta básica en Latch desde la que dar de alta una aplicación (necesitaremos el AppId y el Secret para crear el esquema de supervisión). Además, en el dispositivo A (el que será controlado) será necesario:
  • Tener root.
  • Si tenemos una versión posterior a Android 4.4 tendremos que cambiar el nivel de SELinux a permisivo, podemos utilizar SELinux Mode Changer.
  • Instalar Substrate y confirmar que está funcionando correctamente (lo podemos confirmar si vemos en la aplicación el botón para consultar la galería).
  • Instalar LatchHook. Después de instalar y para que se apliquen los hooks será necesario reiniciar el dispositivo.
  • La app de Latch debe encontrarse instalado en el dispositivo B.

La idea en imágenes

Esquema general de funcionamiento

El flujo de ejecución de la aplicación es sencillo:

  1.  Al arrancar LatchHook en el dispositivo A, se nos pedirá configurar una clave para bloquear el acceso a la configuración de las aplicaciones bloqueadas.
  2. Generamos un código de pareado en el dispositivo B para supervisar el dispositivo A.
  3. Introducir en LatchHook el AppId y Secret de la cuenta de Latch, además del código de pareado generado en B.
  4. Ya tenemos emparejados los dispositivos.
  5. Seleccionamos en LatchHook las aplicaciones que queremos que se bloqueen cuando cerremos el Latch del dispositivo B (el que controlará a A).
  6. Cuando se intente iniciar una aplicación bloqueada en el dispositivo A saltará el bloqueo de LatchHook y en B recibiremos una notificación de intento de acceso no autorizado en Latch.

Una mirada más técnica

Lo que nos permite Substrate es inyectar código en memoria sobre una clase y un método de modo que interceptaremos su llamada y podremos redefinir su comportamiento. Además de llamar al método original si así lo queremos (modificando sus parámetros originales, por ejemplo).

Como el objetivo que perseguimos es impedir que el usuario ejecute una aplicación, un buen punto de partida sería interceptar las llamadas al inicio de aplicaciones realizadas por la aplicación que haga de Launcher en el dispositivo.

Esta aplicación no difiere de cualquier otra aplicación que podamos desarrollar (salvo por tener definida su categoría como android.intent.category.HOME) y está compuesta de actividades que se encargan de arrancar otras actividades ya instaladas en el dispositivo. Así, si queremos interceptar esas ejecuciones desde el punto de vista de Substrate (clase y método), elegiremos los métodos startActivity(…) de la clase android.app.Activity:


Documentación oficial sobre las funciones startActivity

Atacando a este punto, no sólo estaremos interceptando las llamadas al arranque de actividades desde el Launcher por defecto, sino de cualquier aplicación que haga uso de estos métodos para iniciar otras actividades. Dicho esto, un diagrama representativo de lo que hará LatchHook es el siguiente:

Diagrama general de cómo funciona LatchHook

En resumen, las llamadas a los métodos nativos de startActivity serán interceptadas por el hook y evaluadas por el servicio LatchService incluido en LatchHook que se encargará de consultar el estado del pestillo y según esté, ejecutar o bloquear la actividad.

Un detalle que hay que mencionar en la solución, es la necesidad de un servicio al que poder consultar vía IPC. La razón es que cuando salte el hook, lo hará en el contexto de la aplicación que estemos interceptando, y esto tiene varias implicaciones:
  • El código que escribamos en nuestro hook estará limitado a los permisos de la app que hayamos interceptado, así que necesitamos un servicio externo que garantice a través de sus permisos el acceso a internet para consultar los servidores de Latch.
  • El código del hook se ejecuta en el contexto de la aplicación interceptada, de modo que no tendríamos acceso a las SharedPreferences de LatchHook (salvo que las definiéramos con "lectura para todos los usuarios," lo cual desde el punto de vista de la seguridad no es nada recomendable).
Si nos acercamos al código, la base del sistema es la clase Plugin, creada para envolver las funciones utilizadas para hookear con Substrate:

La clase Plugin

Heredando de la clase Plugin sólo tendremos que definir el nombre del plugin, la clase y método que queremos interceptar. Por último la acción alterada que queremos que realice. De este modo, podemos crear un hook sobre el método startActivity(Intent) de la clase Activity del siguiente modo:

Localizando startActivity

Y modificar su comportamiento para reenviar al servicio de Latch cualquier "Intent" enviado a dicho método:

Modificando startActivity

Si observamos este código más en detalle tenemos tres bloques:

  • Recepción de los parámetros por parte de Substrate. Estos son: el método invocado, la instancia sobre la que se ha invocado y el array de parámetros, que en este caso únicamente tendrá un elemento, el "Intent".
  • Un objeto Messenger que utilizaremos para el paso de mensajes con el LatchService. Una vez tengamos la respuesta del servicio se ejecutará el método handleMessage dentro del contexto de la aplicación interceptada, permitiéndonos bloquear la ejecución (contenido del if) o continuar con su ejecución normal (contenido del else)
  • Las últimas tres líneas preparan el Messenger, consultan al servicio y hacen que el método startActivity devuelva null, de modo que se hará un "drop" virtual de la llamada y no se realizará ninguna acción hasta que se tenga respuesta por parte del LatchService.

En cuanto al servicio LatchService, devolverá el estado del Latch si la aplicación es "latcheable" (ha sido seleccionada en la aplicación de LatchHook cuando se configuró):

Devolviendo el estado del Latch

Últimos consejos

La clase LatchServiceHandler se encarga de facilitar la comunicación IPC entre la aplicación hookeada y el LatchService.

El proyecto viene preparado con la clase ConfigurationManager, para poder definir un valor por defecto de AppId y Secret y así ahorrarnos el tener que escribirlo la primera vez:

Añadir aquí los valores de configuración

Los hooks incluidos en esta aplicación son sólo un ejemplo, pero a través de nuevos plugins se podrían controlar fácilmente diferentes puntos del ciclo de vida de una actividad (onCreate, onStart, onResume…), arranque de Services, la recepción de mensajes en un BroadcastReceiver…, y cargarlos en memoria desde la clase PluginManager:



Por último, recordar que aunque no sea infalible y por tanto no válido para escenarios críticos, podría ser un método válido para control de móviles de un tercero bajo muchas otras circunstancias.

Miguel Ángel García
miguelangel.garcia@11paths.com

No hay comentarios:

Publicar un comentario en la entrada