[cubacel] Re: De toDus un poco, ingenieria inversa

  • From: Yurguis <yurguis@xxxxxxxxx>
  • To: Cubacel <cubacel@xxxxxxxxxxxxx>
  • Date: Sat, 30 Jun 2018 12:18:12 -0400

La cantidad de clases y bibliotecas dependen del tamano de la
aplicacion y de la forma en que se defina. Teoricamente una app puede
contener un solo archivo con todas las clases necesarias pero por
problemas de organizacion y comprension se organizan las clases segun
su funcion incluso creando directorios para mejor organizacion. La
ofuscacion del codigo es necesaria cuando hay informacion sensible y/o
metodos como aquellos que generan hashes etc, para autenticacion y no
se quiere que alguien como yo, por ejemplo, pueda saber que esta
pasando porque compromete el uso del servicio, pero segun he visto, no
hay ofuscacion en esta aplicacion.
On Sat, Jun 30, 2018 at 12:10 PM Juan J. Fernandez <juanjfdez@xxxxxxxx> wrote:


Thanks Yurguis y solo queda para los neófitos cono yo entender el porque el
código no debe ser obfuscado y lo otro porque esta aplicación continente
tantas clases y bibliotecas y demás.  O sea , acaso se pudo haber hecho de
firma más simple ese código? , o es que dada futura expansión de ese App
entonces requiera ser tan grande?.


On June 30, 2018 11:57:54 AM Yurguis <yurguis@xxxxxxxxx> wrote:

Recientemente se toco este tema en la lista y me dio la idea de
escribir algo al respecto.

Para aquellos que les interesa este tema aqui les dejo alguna idea de
como funciona la ingenieria inversa aplicada, en este caso, a la
programacion en Android. Mantendre la explicacion lo menos tecnica
posible para que todos puedan llevarse una idea de como funciona.

Lo mas importante para la ingenieria inversa es conocer como funciona
el entorno sobre el cual estoy aplicandola, por lo que si quiero ver
como se esta haciendo algo en Android debo saber al menos como
funciona para poder ir sacando conclusiones.

Para este ejemplo me voy a basar en una aplicacion que hice e instale
en varios moviles para arreglar la WiFi. La aplicacion es sencilla y
basicamente esto es lo que hago.

Cuando el telefono enciende, notifica a la aplicacion que el sistema
esta listo y esta entonces cambia los valores necesarios en el
sistema.

Android tiene un evento para esto que se llama
android.intent.action.BOOT_COMPLETED, cuando el sistema carga,
transmite este evento a todas las aplicaciones instaladas que hayan
notificado que desean recibirlo. La documentacion de Android indica
que para recibir este evento en tu aplicacion debes especificarlo en
el AndroidManifest.xml, archivo necesario en cada apk.

Esta seria la parte correspondiente al AndroidManifest.xml de la aplicacion:

<receiver android:name=".EventReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

Para esto la aplicaion necesita el siguiente permiso:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

Con estos valores, el sistema android ya sabe que esta aplicacion
necesita ser notificada cuando el sistema cargue y especificamente va
a llamar una clase que se llama EventReceiver en mi aplicacion, la
cual se va a encargar de realizar la accion correspondiente al recibir
la notificacion.

Si vamos a la clase EventReceiver, encontramos este codigo:

public void onReceive(Context context, Intent intent) {
String action =  intent.getAction();

if (action.equals("android.intent.action.BOOT_COMPLETED")) {
Log.v("WIFIX_YURGUIS", "android.intent.action.BOOT_COMPLETED event 
received!");
BackgroundIntentService.startActionBootComplete(context);
}
}

Aqui estoy verificando la accion que recibo, y si es la
android.intent.action.BOOT_COMPLETED entonces ejecuto mi accion en
otra clase que se llama BackgroundIntentService.

La documentacion de android en sus ejemplos demuestra como hacer uso
de este esquema de trabajo. La mayoria de los programadores en su
codigo, al necesitar un comportamiento similar, escriben exactamente
el mismo codigo con algunas variaciones que responden principalmente a
su estilo, pero repito, basicamente el codigo es muy similar entre
aplicaciones simplemente porque esta es la recomendacion de Android de
como hacerlo.

Si ahora tomamos la aplicacion y la decompilamos con el apktool,
obtenemos el archivo smali y el codigo correspondiente a esta clase se
veria como.

# virtual methods
.method public onReceive(Landroid/content/Context;Landroid/content/Intent;)V
    .locals 6
    .param p1, "context"    # Landroid/content/Context;
    .param p2, "intent"    # Landroid/content/Intent;

    .prologue
    const/4 v5, 0x0

    const/4 v4, 0x0

    .line 16
    invoke-virtual {p2}, 
Landroid/content/Intent;->getAction()Ljava/lang/String;

    move-result-object v0

    .line 18
    .local v0, "action":Ljava/lang/String;
    const-string v2, "android.intent.action.BOOT_COMPLETED"

    invoke-virtual {v0, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

    move-result v2

    if-eqz v2, :cond_1

    .line 19
    const-string v2, "WIFIX_YURGUIS"

    const-string v3, "android.intent.action.BOOT_COMPLETED event received!"

    invoke-static {v2, v3},
Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I

    .line 20
    invoke-static {p1},
Lcu/yurguis/wifix/BackgroundIntentService;->startActionBootComplete(Landroid/content/Context;)V

    .line 36
    :cond_0
    :goto_0
    return-void
goto :goto_0
.end method

Fijense en el codigo escrito por mi y el resultado de la
decompilacion, si seguimos linea a linea podemos indentifcar
facilmente lo que hace aunque la manera de uno y otro difieren
completamente en su representacion.

Hasta aqui es facil porque tengo el codgio fuente, pero este es el
primer paso para entender lo que pasa en otras aplicaciones.

Si ahora decompilamos la aplicacion toDus, encontramos en su
AndroidManifest.xml lo siguiente:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

<receiver android:exported="false"
android:name="cu.hermes.sdk.android.receivers.HermesBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>

Hmmm, muy parecido al codigo mio. Por tanto, la aplicacion tiene una
clase llamada HermesBootReceiver que es la encargada de recibir la
notificacion. Veamos esa clase.

# virtual methods
.method public onReceive(Landroid/content/Context;Landroid/content/Intent;)V
    .locals 0

    const-string p2, "Device boot complete"

    .line 21
    invoke-static {p2}, Laev;->a(Ljava/lang/String;)V

    .line 22
    invoke-static {p1}, Laez;->a(Landroid/content/Context;)V

    return-void
.end method

Que se esta haciendo aqui? Pues tenemos un mensaje con "Device boot
complete" y estamos invocando el metodo a de la clase aev (aev;->a)
con este mensaje. Aqui, en dependencia de la version del compilador la
decompilacion sera mas o menos explicita, en este caso los metodos
fueron renombrados, esto no es un mecanismo de ofuscacion sino es la
manera del compilador de comprimir el codigo, tal como hacen los
compresores de Javascript.

Si buscamos entre los archivos el que tiene nombre aev vemos que
originalmente se llama HermesLogger.java, por tanto aqui no estamos
mas que escribiendo en un registro que hemos recibido la accion
BOOT_COMPLETED, similar a lo que tengo hecho en mi codigo solo que no
hago uso de una clase para esto.

Seguimos con el codigo y entontramos otra clase de nombre aez
invocando el metodo a de esa clase. Esta clase se llama
HermesServiceManager.class y es la encargada de manejar los servicios
de la aplicacion, la clase de mi codigo que hace esto se llama
BackgroundIntentService.

Por tanto, si tratasemos de escribir ese codigo, deberia quedar mas o
menos de esta forma. Notese que los nombres de las clases los conozco,
pero el nombre de los metodos el compilador los renombro y aqui
utilice nombres que utilizaria yo, pero esto no es importante ya que
la idea de lo que se esta haciendo es lo principal.

public void onReceive(Context context, Intent intent) {
    HermesLogger.log("Device boot complete");
HermesServiceManager.startActionBootComplete(context);
}

El objetivo de esta parte era mostrar como se utiliza la ingenieria
inversa y como es el proceso mas o menos de ir adivinando que hizo el
programdor. Este es un ejemplo bien sencillo pero lo aplicado aqui es
valido para cualquier otros casos, con codigos mas extensos, lo cual
llevaria a mas tiempo de analisis.

Si lo desean podriamos seguir profundizando en este tema o en esta
aplicacion con fines educativos, aunque no soy un experto en esto,
tengo algo de experiencia y con la ayuda de otros que conozcan del
tema podemos mostrar temas relacionados.





Other related posts: