3.1

Enginyeria Inversa i el Programari Lliure

Abans de parlar de protecció, cal entendre la filosofia del codi. La Free Software Foundation (FSF) defineix quatre llibertats bàsiques que tot programari lliure hauria de garantir:

▶️

Llibertat 0 — Execució

Poder executar el programa per a qualsevol propòsit, sense restriccions.

🔍

Llibertat 1 — Coneixement

Poder estudiar com funciona el programa i adaptar-lo a les pròpies necessitats. Requereix accés al codi font.

🤝

Llibertat 2 — Compartir

Redistribuir còpies del programa per ajudar als altres.

✏️

Llibertat 3 — Modificar

Distribuir còpies de les pròpies versions modificades, permetent a la comunitat beneficiar-se de les millores.

⚠️ El repte del programari privatiu

En les apps mòbils, les empreses solen voler protegir el seu codi font com a propietat intel·lectual. Distribuir el binari sense el codi és la primera línia de defensa, però no és suficient per si sola.

🔬 Enginyeria Inversa

És el procés d'intentar recuperar el codi font original a partir d'un fitxer executable (el binari que es baixa de la store). Eines com JADX, apktool o Procyon permeten fer-ho de forma quasi automàtica.

3.2

El Procés de Construcció (Build) d'una App Android

Per entendre com es protegeix una app, primer cal saber com es fabrica. El procés de creació d'un fitxer APK (Android Package) és significativament més complex que una compilació de PC tradicional:

1️⃣

Compilació Java/Kotlin → Bytecode

El codi font es compila a bytecode estàndard de la JVM (fitxers .class) usant el compilador javac o kotlinc. En aquest punt, el codi és portable però encara no es pot executar a Android.

2️⃣

Conversió a Dalvik (DEX)

L'eina d8 (successor de dx) converteix els fitxers .class en un únic fitxer classes.dex (Dalvik Executable), optimitzat per funcionar a l'ART d'Android i consumir menys memòria.

3️⃣

Empaquetament de Recursos

L'eina aapt agafa el codi .dex i els recursos (imatges, fitxers XML, layouts) i els comprimeix junts dins d'un fitxer ZIP anomenat APK. És literalment un arxiu comprimit.

4️⃣

Signatura de l'APK

Es signa amb la clau privada del desenvolupador usant apksigner. Sense signatura, el mòbil refusarà la instal·lació. La signatura valida la integritat i l'origen de l'app.

5️⃣

Alineament (zipalign)

S'optimitza l'estructura interna de l'APK perquè el sistema operatiu pugui llegir els recursos de forma eficient, consumint menys RAM en temps d'execució. És el pas final abans de la distribució.

El diagrama mostra el flux de construcció complet d'una app Android. A l'esquerra hi ha dues entrades separades: el codi font (Java/Kotlin) i els recursos (imatges, XMLs). El codi passa per la compilació fins a generar un fitxer .dex, mentre que els recursos es processen amb aapt. Ambdues sortides es fusionen en un APK no signat, que posteriorment rep la signatura digital i es processa amb zipalign per donar l'APK final llest per distribuir.

3.3

Descompilació: El Camí de Tornada

El problema fonamental del model APK és que qualsevol persona pot descarregar l'arxiu i analitzar-lo. Un atacant pot passar un APK per un descompilador i obtenir una aproximació llegible del codi original:

🔎 JADX

Descompilador gratuït i de codi obert que converteix el codi DEX d'un APK directament en codi Java llegible. Disposa d'interfície gràfica i de línia de comandes.

🔧 apktool

Permet desempaquetar i reempaquetar APKs. Útil per accedir als recursos (imatges, XMLs) i al codi Smali (assembler de Dalvik), el format de baix nivell de les apps Android.

💡

Pseudocodi, no codi original

El resultat de la descompilació no és exactament el codi font original (es perden comentaris i sovint noms de variables). Però sí que és un pseudocodi lleigble que permet entendre perfectament la lògica de l'app, les APIs que crida i on guarda les dades.

🚨

Risc: App en mode Debug a producció

Si s'allibera una app en mode debug en lloc de release, la descompilació és extremadament fàcil perquè el fitxer inclou informació extra per a programadors: noms de variables, comentaris, traces de la pila, etc. Mai publicar una build de debug a la store.

Procés de descompilació d'una app
Fig 3.2 – El camí simètric: Codi font ➔ Compilació ➔ Executable ➔ Descompilació ➔ Pseudocodi

El diagrama il·lustra la simetria del procés: la part superior mostra el camí normal del desenvolupador (codi font → compilació → executable distribuïble); la part inferior mostra el camí invers de l'atacant (APK → descompilador → pseudocodi llegible). La distància entre el pseudocodi i el codi original depèn directament del nivell d'ofuscació aplicat. Sense ofuscació, la línia de separació pràcticament desapareix.

3.4

Tècniques d'Ofuscació

L'ofuscació consisteix a fer que el codi sigui difícil d'entendre per a humans i màquines, sense canviar-ne el funcionament. No és una solució definitiva, però augmenta significativament el cost i el temps necessari per a un atacant:

🔤

Ofuscació de Noms (Minificació)

La més comuna. Canvia noms llargs i descriptius per noms curts i sense significat:

autenticarUsuariAmbToken()a()

Si es descompila, l'atacant veu milers de funcions anomenades a(), b(), c(), fent la lògica gairebé impossible de seguir.

💀

Confusió de Dades i Codi Mort

S'insereix codi mort (blocs que mai s'executen) o "comentaris trampa" que despistin els descompiladors automàtics i dificulten el seguiment del flux real del programa.

🎨

Double Coding (Codificació Creativa)

Escriure codi de forma extremadament creativa que aprofita les rareses del llenguatge. Molt comú en Python o JavaScript amb competicions de codi ofuscat.

Exemple de Hello World ofuscat en Python
Fig 3.3 – Exemple de "Hello World" ofuscat en Python

La imatge mostra dues versions del mateix programa. A l'esquerra, el codi original (unes dues línies trivials que imprimeixen un text simple). A la dreta, la versió ofuscada: un galimaties de símbols, operadors i literals numèrics que resulta completament il·legible. Malgrat l'aparença caòtica, ambdós programes produeixen exactament el mateix resultat en executar-se. Aquest contrast demostra visualment el poder i la filosofia de l'ofuscació.

3.5

Eines d'Ofuscació en Producció

🌐 JavaScript — UglifyJS

Per a codi interpretat com JavaScript, s'usa UglifyJS. Realitza tres operacions en cadena:

  • Minificació: Elimina espais en blanc, salts de línia i comentaris.
  • Ofuscació de noms: Canvia variables i funcions per identificadors curts.
  • Compressió: Tot queda en una sola línia il·legible.

🔗 UglifyJS a GitHub

🤖 Android — R8 (ProGuard)

L'eina oficial per a Android és R8, integrada a Android Studio (successor de ProGuard). Es configura al fitxer build.gradle.kts:

buildTypes {
    release {
        isMinifyEnabled = true
        proguardFiles(
            getDefaultProguardFile(
                "proguard-android-optimize.txt"
            ),
            "proguard-rules.pro"
        )
    }
}

La directiva isMinifyEnabled = true activa l'ofuscació i l'optimització de codi en les builds de release.

El diagrama mostra com R8 (o ProGuard) processa el codi en quatre etapes sequencials. Primer, la fase de Minificació elimina tot el codi i les llibreries que no s'usen (tree shaking). Després, l'Optimització reescriu el codi per fer-lo més eficient. A continuació, l'Ofuscació canvia els noms de totes les classes i mètodes per lletres sense sentit. Finalment, la Preverificació comprova que el resultat és vàlid. El fitxer final és significativament més petit i molt més difícil de llegir.

3.6

Optimització vs. Seguretat: El Balanç

L'ofuscació sovint va lligada a l'optimització del compilador. En C/C++, els compiladors (GCC, Clang) accepten nivells d'agressivitat configurables:

Nivell Nom Efecte Risc
-O0 Cap optimització Compilació ràpida, fàcil de depurar Codi lent i gran (DEBUG)
-O1 Bàsica Redueix mida i millora velocitat lleument Baix
-O2 Estàndard Equilibri entre velocitat i compatibilitat Baix-Mig
-O3 Agressiva Màxima velocitat, codi molt diferent del font Alt: pot trencar l'app

✅ Avantatges

El codi és més ràpid en execució i el binari distribuïble és més petit, reduint el temps de descàrrega i l'ús de memòria.

⚠️ Inconvenients

Si l'ofuscació és massa agressiva, l'app pot deixar de funcionar, especialment si depèn de noms de classes o mètodes per a la comunicació amb servidors externs via reflection.

💡

Fitxer de regles ProGuard (proguard-rules.pro)

Per evitar que R8 ofusqui classes que no ha d'ofuscar (com models de dades que es serialitzen per a una API), s'usen regles -keep. Per exemple: -keep class com.empresa.app.models.** { *; }. Gestionar correctament aquest fitxer és crucial per evitar errors en producció.

🗂️ Resum Ràpid del Mòdul

Enginyeria InversaRecuperació del codi llegible a partir del binari amb eines com JADX o apktool.
Build Android (APK)Compilació → DEX (.d8) → Empaquetament (aapt) → Signatura → Zipalign.
Ofuscació de NomsCanvia autenticarUsuari() per a(), fent el codi descompilat il·legible.
R8 / ProGuardEina integrada a Android Studio: minifica, optimitza i ofusca amb isMinifyEnabled.
UglifyJSOfuscador estàndard per a JavaScript: elimina espais, noms llargs i comentaris.
-O3 / proguard-rulesL'ofuscació excessiva pot trencar l'app; cal usar regles -keep per protegir les classes crítiques.