contenido de proc/maps

Detección de carga dinámica en aplicaciones de Android con /proc/maps

TL; DR: A través de la carga dinámica, los autores de malware pueden cargar de forma encubierta código malicioso en su aplicación para evitar la detección. Podemos detectar dicha carga a través del archivo generado por el kernel /proc/[PID]/maps de la aplicación.
Recientemente, creamos un script simple que nos permite detectar la carga dinámica en las aplicaciones de Android. Esto nos presentó una buena oportunidad para discutir la carga dinámica en general en este blog. 

Carga dinámica y vinculación:

Para comprender el resto del blog, es importante repasar los conceptos básicos de la vinculación y la carga, y resaltar las diferencias entre la vinculación y la carga, y entre estática y dinámica.

Qué es vincular:

El proceso de compilación tiene varias partes. La vinculación es el último paso antes de obtener un ejecutable ejecutable. Un programa suele ser más que un simple archivo autónomo y depende de otras bibliotecas o archivos para funcionar. Como tal, no es suficiente que compile su código en un código de máquina para poder ejecutarlo, sino también de alguna manera liga los diferentes archivos en un ejecutable cohesivo.

¿Cómo se hace esto en la práctica? Después de la compilación y el ensamblaje, el ensamblador genera archivos de objetos, que generalmente corresponden a cada módulo de su programa. Dichos archivos pueden ser reubicables o ejecutables. Los archivos de objetos de la primera variedad deben vincularse antes de que puedan ejecutarse, pero los del segundo tipo pueden ejecutarse inmediatamente. En el ecosistema GNU/Linux, la extensión habitual de un archivo objeto es .o. Los archivos de objetos compartidos, conocidos como bibliotecas, son archivos de objetos reubicables que están destinados a ser utilizados por muchos programas diferentes y no se pueden ejecutar por sí solos. En GNU/Linux tienen la extensión .so (Shared Object), mientras que en Windows tienen la extensión .dll (Dynamic-link Library).

Luego, el enlazador toma los archivos, resuelve los símbolos (funciones y variables) y los señala a las direcciones de memoria correctas escribiendo todo en la tabla de símbolos del ejecutable. En aras del rendimiento, también reubica su código para que las piezas de código relacionadas terminen asignadas a direcciones de memoria cercanas, independientemente de cómo haya organizado originalmente su programa, ya que la legibilidad humana ya no es un problema y el rendimiento es la principal prioridad. 

Los muchos nombres diferentes del ejecutable:

Tanto en Linux como en Windows, esas extensiones aparentemente diferentes tienen los mismos formatos subyacentes que los ejecutables. En Windows son PE (Portable Executable) y suelen tener las extensiones .exe y .dll, y en Linux son ELF (Executable and Linkable File) y tienen las extensiones .bin, .so o .o (Sí , los archivos de objetos que mencionamos anteriormente también son ELF).

Enlace dinámico:

Decimos que algo ha sido enlazado dinámicamente cuando en lugar de hacer ese proceso durante el tiempo de compilación, se hace en tiempo de carga o tiempo de ejecución. Lo que nos lleva a la carga: un cargador simplemente copia el contenido de la salida del enlazador en la memoria y ejecuta el programa. Cuando se utiliza la vinculación dinámica, el proceso de vinculación ocurre justo antes de la carga, cuando se ejecuta el programa, lo que a menudo genera confusión entre los dos.
La carga dinámica, por otro lado, significa que partes del código se pueden cargar en la memoria en cualquier momento durante el tiempo de ejecución. Los dos procesos pueden y con frecuencia se realizan juntos.

¿Por qué es esto un problema?

La carga dinámica es ciertamente útil. Por ejemplo, en lugar de cargar todas las bibliotecas que usará su programa en el momento de la carga, puede cargarlas solo cuando necesite usarlas, por lo tanto, usar menos memoria, o solo cargarlas condicionalmente en ciertos casos.

Pero también presenta una manera fácil para que los desarrolladores de malware oculten su código malicioso. Pueden poner todo su código legítimo en el APK y mover todo el código nefasto a un DEX (Dalvik Executable) que la aplicación descargará y luego cargará dinámicamente durante el uso y así hacer que su aplicación parezca inocua en la inspección estática básica del APK.

Cómo se cargan las clases DEX:

Android ofrece una opción para cargar dinámicamente archivos .dex usando una clase llamada Cargador de clases Dex. Para cargar una clase, simplemente necesitamos escribir:

// Init the loader
DexClassLoader dexClassLoader = new DexClassLoader(path_to_dex, null, null, parent_class);

// Load the class:
Class dynamic_class = dexClassLoader.loadClass("DynamicClass");

// Load a method we could call it
Method method = dynamic_class.getMethod("method1");

Y luego podemos usar el método de invocación () para usar el método.

El archivo /proc/[PID]/maps:

En Unix, todo es un archivo, e incluso si no es realmente uno, se maneja y se accede a él como tal. Esto incluye las estructuras de datos del Kernel y Linux no es una excepción a la regla. El Kernel de Linux nos permite acceder y leer sus estructuras de datos a través del /proc/ sistema de pseudo-archivo. Cada proceso tiene su propia carpeta en /proc/[PID]. Los archivos y subcarpetas contienen mucha información útil e importante sobre el proceso, pero hoy nos centraremos en un solo archivo: /proc/[PID]/maps.

/proc/[PID]/maps muestra un gráfico de la memoria asignada de un proceso. Cuando decimos memoria mapeada, nos referimos a un segmento de memoria virtual que tiene una correspondencia uno a uno con un archivo. Esta asignación permite que una aplicación modifique y acceda a archivos leyendo y escribiendo directamente en la memoria. Esto significa que cuando un programa acceda a un archivo, este terminará siendo registrado en su archivo /proc/[PID]/maps.

/proc/[PID]/maps también nos muestra qué permisos tiene el proceso para cada segmento. Esto puede ayudarnos a determinar qué archivos editó el proceso y qué archivos leyó.

Así es como se ve un segmento corto de un archivo normal /proc/PID/maps:

7f9cefbf7000-7f9cefbf8000 r--p 00000000 103:03 1589169            /usr/lib/libXcomposite.so.1.0.0
7f9cefbf8000-7f9cefbf9000 r-xp 00001000 103:03 1589169            /usr/lib/libXcomposite.so.1.0.0
7f9cefbf9000-7f9cefbfa000 r--p 00002000 103:03 1589169            /usr/lib/libXcomposite.so.1.0.0
7f9cefbfa000-7f9cefbfb000 r--p 00002000 103:03 1589169            /usr/lib/libXcomposite.so.1.0.0
7f9cefbfb000-7f9cefbfc000 rw-p 00003000 103:03 1589169            /usr/lib/libXcomposite.so.1.0.0
7f9cefbfc000-7f9cefc08000 r--p 00000000 103:03 1579223            /usr/lib/libxcb.so.1.1.0
7f9cefc08000-7f9cefc1b000 r-xp 0000c000 103:03 1579223            /usr/lib/libxcb.so.1.1.0
7f9cefc1b000-7f9cefc24000 r--p 0001f000 103:03 1579223            /usr/lib/libxcb.so.1.1.0
7f9cefc24000-7f9cefc25000 r--p 00027000 103:03 1579223            /usr/lib/libxcb.so.1.1.0
7f9cefc25000-7f9cefc26000 rw-p 00028000 103:03 1579223            /usr/lib/libxcb.so.1.1.0
7f9cefc26000-7f9cefc27000 r--p 00000000 103:03 1577111            /usr/lib/libX11-xcb.so.1.0.0
7f9cefc27000-7f9cefc28000 r-xp 00001000 103:03 1577111            /usr/lib/libX11-xcb.so.1.0.0
7f9cefc28000-7f9cefc29000 r--p 00002000 103:03 1577111            /usr/lib/libX11-xcb.so.1.0.0
7f9cefc29000-7f9cefc2a000 r--p 00002000 103:03 1577111            /usr/lib/libX11-xcb.so.1.0.0
7f9cefc2a000-7f9cefc2b000 rw-p 00003000 103:03 1577111            /usr/lib/libX11-xcb.so.1.0.0
7f9cefc2b000-7f9cefc47000 r--p 00000000 103:03 1584005            /usr/lib/libX11.so.6.3.0
7f9cefc47000-7f9cefcd1000 r-xp 0001c000 103:03 1584005            /usr/lib/libX11.so.6.3.0

Cada fila del archivo registra un solo segmento de memoria en el espacio de direcciones de memoria virtual contiguo asignado al proceso.

  • Dirección – La primera columna muestra la dirección inicial y final del segmento.
  • Permisos – Esta columna muestra qué permisos tiene el proceso para el segmento. r/w/x son los habituales de lectura/escritura/ejecución, mientras que la última letra es s or p, que significa compartida o privada, respectivamente.
  • Compensación – Este es el desplazamiento desde el principio del archivo, para poder calcular la dirección inicial de los datos mapeados. A veces, un segmento no se asigna desde un archivo (en cuyo caso, la columna de la ruta tendrá un identificador de la naturaleza del segmento, como se explica a continuación), en cuyo caso el desplazamiento simplemente se deja en 0.
  • Dispositivo – Cuando el segmento se asignó desde un archivo, el número hexadecimal del dispositivo donde se almacena el archivo se muestra en esta columna.
  • Inodo (Nodo de índice) – Si el segmento proviene de un archivo, este es el número de inodo del archivo.
  • Path – Esta es la ruta del archivo, si existe. Esto puede ser [montón], [pila] o [vsdo] si el segmento es la estructura del mismo nombre.

Nuestro humilde guión:

En las aplicaciones ofensivas de Android, la carga dinámica del código generalmente se realiza desde el directorio de inicio de la aplicación, por lo que los archivos cargados en la memoria desde ese diccionario (u otras ubicaciones comunes) deberían aparecer en el archivo de mapas. Para automatizar la tarea de verificar ese archivo, construimos un script muy simple que usa expresiones regulares para buscar la cadena '/data/data' en el archivo de mapas de un PID dado en el dispositivo conectado y devuelve las líneas que coinciden. /data/data es, por supuesto, el directorio de inicio de las aplicaciones donde se almacenan los archivos y los datos. Es la única ubicación desde la que una aplicación puede cargar archivos DEX.

Interactuar con ADB a través de Python fue a veces un desafío, ya que hacerlo usando el shell puede ser incómodo, pero encontramos un conjunto útil de herramientas llamado herramientaspwn que proporcionan exactamente esa funcionalidad. Ofrecen un amplio conjunto de funcionalidades diseñadas para ayudar con la piratería y la creación de prototipos. Definitivamente échale un vistazo.

Puede ver el script completo que usamos en este Esencia de github.

Ir al contenido