Mòdul 3: Ofuscació de Codi i Enginyeria Inversa
Tècniques per protegir la propietat intel·lectual de les apps contra la descompilació i l'anàlisi estàtica.
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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
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ó.
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.
🤖 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.
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
autenticarUsuari() per a(), fent el
codi descompilat il·legible.isMinifyEnabled.-keep per protegir les classes crítiques.