DexPatcher: parchear APK de Android con Java

Probablemente haya visto o instalado aplicaciones modificadas, ya sea un marcador parcheado para su resolución o una versión personalizada de WhatsApp con características adicionales. Sin embargo, ¿cómo hacen eso los desarrolladores? Muchas veces, el código fuente de las aplicaciones ni siquiera está disponible, entonces, ¿cómo funciona todo? Primero veremos eso, luego veremos una nueva herramienta que tiene como objetivo hacer que el proceso sea mucho más fácil, y finalmente compárelo con el popular marco Xposed para ver cómo difieren.

Es posible que haya escuchado sobre cómo los APK generalmente se modifican: los desarrolladores se conectan a la matriz, comienzan a ver todo en Smali y obtienen la capacidad de modificar cosas utilizando el poder de la Fuente. Una llamada telefónica es suficiente para sacarlos una vez hecho, en ese momento están listos para compartir los nuevos APK brillantes.

Más en serio ... Comencemos por el principio. Si no está familiarizado con la modificación de aplicaciones de Android, es posible que se pregunte qué es pequeño. Los desarrolladores generalmente usan el lenguaje de programación Java para codificar aplicaciones de Android. Un programa (el compilador) luego "traduce" ese código a otro formato adecuado para su dispositivo, lo que resulta en archivos .dex contenidos dentro del paquete de la aplicación (o APK).

En ese momento, ya no puede acceder al código fuente original (a menos que sea el desarrollador o la aplicación sea de código abierto). Sin embargo, lo que sí tienes es el APK, ya que es lo que está instalado en tu dispositivo. A partir de él, puede obtener los archivos dex (generalmente classes.dex) y luego tratar de traducirlo nuevamente a un formato que pueda entender. Ahí es donde entra en juego smali, como una traducción más legible pero fiel. Puede ir un paso más allá y traducirlo de nuevo a Java, aunque ese proceso no es lo suficientemente fiel: obtendrá un resultado comprensible, pero es probable que no pueda traducirlo al revés como algunos detalles se perderá en el camino. En otras palabras, cualquier modificación que pueda hacer será en vano, ya que no podrá volver a convertirla en un APK para instalarla en su dispositivo ... al menos no sin mucho esfuerzo.

smali / baksmali es en realidad un ensamblador / diseminador para el formato dex, eso es lo que significa literalmente en islandés. Sin embargo, generalmente nos referimos al formato que smali entiende cuando decimos 'Smali' (piense en ello como instrucciones que definen cada pequeño detalle, incluso si no todos lo necesitamos los humanos; por lo tanto, es más detallado que Java). También tenga en cuenta que la explicación anterior está un poco simplificada, pero debería ser una analogía cercana a la vez que es fácil de entender.

Entonces, ¿qué necesitaría hacer un desarrollador para modificar una aplicación (sin acceso a la fuente)? El proceso es más o menos como sigue:

  1. Obtenga el APK (desde la web o desde el dispositivo).
  2. Use algo como apktool para descompilar el APK en Smali. (apktool hace uso de smali / baksmali, pero hace que sea mucho más fácil descompilar y reconstruir APK, y también se encarga de decodificar recursos como archivos XML).
  3. Extraiga classes.dex del APK, luego use dex2jar y finalmente un descompilador Java para obtener un código Java (incompleto, a menudo roto, pero en su mayoría comprensible). (Esto es opcional, pero puede ser útil ya que Smali es mucho más difícil de entender).
  4. Identifica qué modificar.
  5. Realmente modifíquelo editando el código Smali directamente.
  6. Alternativamente, escriba la modificación en Java, compílela, descompílela nuevamente en Smali, luego copie el código Smali resultante.
  7. Una vez que todo haya terminado, use apktool nuevamente para reconstruir el APK.
  8. Firme el APK (para verificar la identidad del autor; todos los paquetes deben estar firmados) y finalmente instálelo.

Escribir código Smali es bastante difícil y propenso a errores. Se pueden hacer cambios más pequeños en Smali, pero agregar nuevas características es más difícil. Además, no tendrá errores de tiempo de compilación, por lo que incluso los errores tipográficos solo pueden detectarse en tiempo de ejecución. Expandir y compartir parches de Smali también puede ser problemático, ya que las diferencias tienden a ser muy específicas para una versión de APK en particular. Aunque existen algunas herramientas para hacer que las partes del proceso explicadas anteriormente sean más fáciles (viene a la mente Virtuous Ten Studio), aún puede ser agotador.

DexPatcher por el miembro senior Lanchon tiene como objetivo solucionar estos problemas, simplificando el proceso y permitiendo a los desarrolladores evitar por completo tratar con Smali. En cambio, los desarrolladores pueden escribir parches solo en Java y hacer que DexPatcher se encargue de todo lo demás.

Esto tiene la principal ventaja de tener archivos de parche fáciles de leer y manejar. Parchear APK también se vuelve más conveniente en general. Veremos un ejemplo completo sobre cómo usar DexPatcher en un momento, pero aquí hay una descripción rápida de lo que ofrece primero:

  • Fuente abierta.
  • Multiplataforma: debe ejecutarse en Linux, Mac y Windows.
  • Archivos de parche: las modificaciones que realice están contenidas en archivos de parche de Java que puede compartir independientemente.
  • Java: no es Smali.

También obtiene la ventaja de la comprobación de errores en tiempo de construcción, por lo que los errores aparecen al principio del ciclo de desarrollo. El compilado de Java proporciona su comprobación de tiempo de compilación habitual (con acceso a los símbolos APK originales), y DexPatcher hace cumplir la compatibilidad de la fuente y el parche al parchear, proporcionando información útil y advertencias cuando parece estar haciendo algo legal pero sospechoso.

Además de eso, DexPatcher viene con un conjunto de scripts de ayuda (solo disponible en Linux, aunque también se pueden transferir a otras plataformas). Estos se encargan de configurar el espacio de trabajo, extraer las clases y recursos del APK objetivo, descompilar las clases en Java (el descompilador CFR Java se usa para este último) y finalmente construir y firmar el APK parcheado una vez que haya terminado.

Echemos un vistazo a un ejemplo (en Linux):

Instalar los scripts de DexPatcher

 $ # Haga un directorio donde podamos probar cosas e ingresarlo. $ mkdir xda-test $ cd xda-test $ git clone //github.com/Lanchon/DexPatcher-scripts.git dexpatcher # Clone el repositorio de scripts de ayuda de DexPatcher. $ cd dexpatcher $ chmod + x dxp- * # No es necesario, pero para mayor claridad: debemos asegurarnos de que los archivos que llamaremos más tarde sean ejecutables. 

Configurar los scripts de DexPatcher

Abra dxp.config en su editor de texto favorito y asegúrese de cambiar las variables necesarias para adaptarlas a su sistema. Solo necesita cambiar la siguiente línea para que apunte a la ubicación de instalación de su SDK de Android:

 dxp_android_sdk_dir = (~ / android / sdk) 

(DexPatcher elegirá automáticamente la versión de plataforma más alta disponible. Además, también puede modificar otras opciones de configuración para que use sus propias versiones de algunas herramientas en lugar de los valores predeterminados incluidos).

Para facilitar el acceso, podemos agregar el directorio dexpatcher a nuestra RUTA, o incluso vincular los diferentes scripts dxp- * a una ubicación que ya está en su RUTA, como ~ / bin:

 export RUTA = $ PWD: $ RUTA 

Modificar una aplicación

Para este ejemplo, usaremos una aplicación simple y de código abierto. Por supuesto, sería posible parchear el código fuente directamente en este caso particular, ¡pero eso no es nada divertido!

Tomaremos la aplicación "Obtener ID" de basil2style, una aplicación que le muestra algunos detalles sobre su dispositivo. Nuestro objetivo es modificar el botón "Copiar" para la "ID del dispositivo" y hacer que comparta esta ID en su lugar:

  • Primero, descarguemos el APK que vamos a modificar: Obtener ID.
  • Descompilar la aplicación.
  • Cree la clave de firma que luego usaremos para firmar el APK.

También podemos hacerlo todo a través del shell, utilizando los scripts de ayuda:

 $ cd dexpatcher # Ir a nuestro directorio de trabajo. $ curl -O //f-droid.org/repo/makeinfo.com.getid_1.apk # Descarga el APK. $ dxp-setup-for-apk makeinfo.com.getid_1.apk # Desempaquete y descompile el APK. $ cd makeinfo.com.getid_1 # Vaya al directorio recién creado donde se descomprime / descomprime todo. $ dxp-create-keystore # Crea la clave de firma de APK. Presione 6 veces (o complete la información), luego "sí". 

Notará algunos directorios diferentes allí:

  • decodificar : encontrará los recursos y Smali aquí, tal como lo decodificó apktool.
  • src : directorio vacío. Aquí es donde colocaremos nuestros archivos de parche.
  • src-cfr : aquí es donde cfr descompuso la aplicación (junto con los errores). Es un buen lugar para mirar y decidir qué cambiar (es posible que también necesite recursos y sus ID del directorio de decodificación anterior, pero no para este ejemplo en particular).
  • src-cfr-nodecode : igual que el anterior, pero que contiene solo apéndices vacíos (sin código, solo esqueletos). Puede usar estos archivos como base para su parche, como veremos en un momento.

Como hemos mencionado antes, queremos cambiar el botón "Copiar" de la ID del dispositivo para compartir el texto de la ID. Si miramos alrededor del código fuente, notaremos que el evento onClick del botón Device Copy Copy (device_copy) es manejado por una clase anónima en src-cfr / makeinfo / com / getid / MainActivity.java. Si bien podríamos modificarlo aquí, generalmente es mejor encontrar una forma alternativa de hacerlo, ya que las clases anónimas tienen nombres numéricos (MainClassName $ SomeNumber, por ejemplo MainActivity $ 3) que pueden cambiar de manera impredecible entre las versiones.

En cambio, registraremos nuestra propia clase para el evento modificando la clase MainActivity. Primero, copiemos la versión "esqueleto" de src-cfr-nocode / makeinfo / com / getid / MainActivity.java a src / makeinfo / com / getid / MainActivity.java (recuerde que src es donde vivirá nuestro parche). (También puede copiar la versión con el código completo si lo prefiere, esto es solo cuestión de gustos).

Ahora podemos editarlo de la siguiente manera:

  • Agregue la importación necesaria para la anotación DexPatcher:
 import lanchon.dexpatcher.annotation. *; 
  • Agregue una etiqueta para indicar que estamos editando la clase. También establecemos la acción predeterminada para los miembros de la clase de parche en IGNORE, lo que significa que nuestro código hará referencia a los miembros durante la compilación de Java, pero DexPatcher los ignorará.
 @DexEdit (defaultAction = DexAction. IGNORE) public class MainActivity // La referencia a ActionBarActivity se satisfará mediante símbolos // extraídos de la aplicación cuando construyamos el parche. extiende ActionBarActivity { 
  • Además, agregue cuerpos vacíos al constructor y al método onCreate, así como a todos los demás métodos que planeamos usar (recuerde que se ignorarán cuando se aplique realmente nuestro parche; solo los estamos agregando para que podamos consultarlos aquí si es necesario) También puede agregar la palabra clave nativa en su lugar.
  • Ya podemos construir el parche en este punto, si tienes curiosidad:
     $ dxp-make # Salida: `patched.apk`. 

    Bastante simple, ¿verdad? Sin embargo, sigamos adelante, todavía no hemos terminado.

  • Editemos onCreate ahora para establecer su propio OnClickListener para que podamos compartir el ID del dispositivo en lugar de copiarlo en el portapapeles:
     // Cambie el nombre del método de destino para que podamos llamarlo (el original) // si es necesario. @DexEdit (target = "onCreate") protegido void source_onCreate (Bundle var1) {} // Agregue nuestro nuevo método personalizado. @Override @DexAdd protected void onCreate (Bundle var1) {// Llame al método original: source_onCreate (var1); // Reemplaza el texto y el controlador: device_copy. setText ("Compartir"); copia_dispositivo. setOnClickListener (nuevo DeviceCopyOnClick ()); } // Tenga en cuenta que no usamos una clase anónima para evitar el choque de nombres con // MainActivity $ 1, que ya existe. // También podríamos haber definido una clase MainActivity.Patch anidada y haber usado // una clase anónima en MainActivity.Patch.onCreate (), y luego llamada // MainActivity.Patch.onCreate () desde MainActivity.onCreate (). La clase @DexAdd DeviceCopyOnClick implementa View. OnClickListener {@Override public void onClick (Ver objeto) {if (MainActivity. This. Val) {Intent intent = new Intent (Intent. ACTION_SEND); intención setType ("texto / sin formato"); intención putExtra (Intención. EXTRA_SUBJECT, "ID del dispositivo"); intención putExtra (Intención. EXTRA_TEXT, dispositivo. getText (). toString ()); startActivity (Intento. createChooser (intento, "Compartir ID de dispositivo")); } else {Brindis. makeText (MainActivity. this. getApplicationContext (), "Nada que compartir", 0). espectáculo (); }}} 
  • Parece que hemos terminado ahora! El parche completo debería verse así. Ahora podemos construir el APK parcheado e instalarlo:
     $ dxp-make $ adb install patched.apk 
  • Echemos un vistazo al resultado:

(¡Gracias a Lanchon por ayudarme con el código de muestra!)

Xposed es inmensamente popular, y por una buena razón: hace que construir, compartir e instalar mods sea mucho más simple para desarrolladores y usuarios por igual. Hay algunas diferencias entre DexPatcher y Xposed que pueden hacer que algunos prefieran uno sobre el otro:

  1. Xposed hace su magia enganchando métodos en tiempo de ejecución y permitiendo a los desarrolladores hacer algo antes, después o en su lugar cualquier método. DexPatcher, por otro lado, modifica todo antes del tiempo de ejecución y produce un APK modificado y autónomo; el código de ejecución antes, después o en lugar de los métodos aún es posible, y en realidad tienes algo de libertad adicional.
  2. Producir un APK independiente significa que no depende de ningún marco externo. Esto también significa que no se requiere root para modificar las aplicaciones del usuario.
  3. Dado que ha creado un nuevo APK con DexPatcher, se firmará de manera diferente. Esto significa que los usuarios no pueden recibir actualizaciones oficiales del autor original, y pueden causar algunos problemas con aplicaciones como Google Apps si las firmas están marcadas.
  4. El código fuente de los parches de los módulos y los parches DexPatcher se puede distribuir y modificar fácilmente. También comparten muchas similitudes si te familiarizas un poco con cada una.

Hemos hablado lo suficiente sobre DexPatcher. ¡Ahora es tu turno de probarlo, así que dirígete al Foro de DexPatcher para comenzar de inmediato!