τTau SolutionsOfuscación

Ofuscadores comerciales

referencia técnica · Revisado el 12 de agosto de 2026

1. Qué es y por qué existe

R8 renombra (R8 y ProGuard). Un ofuscador comercial hace eso y además cifra, aplana, inserta ruido y esconde llamadas, y en algunos casos vende junto a ello comprobaciones de integridad en ejecución. La diferencia práctica para quien analiza es que con R8 el código decompilado es correcto y feo, y con un ofuscador comercial el decompilador puede directamente no producir código válido.

Este documento cataloga las transformaciones —que es la parte reutilizable, porque los productos van y vienen pero el repertorio es finito—, describe los cuatro productos relevantes y publica una tabla de huellas. Es el complemento de Packers y protectores, que trata el caso en el que ya no hay código que decompilar porque viaja cifrado.

Alcance. Se documenta qué transformación aplica cada producto y cómo se identifica. No se documenta cómo revertir ninguna, ni cómo neutralizar las comprobaciones de integridad que algunos añaden. La sección 8 se acerca a la frontera al tratar el descifrado de cadenas y se queda del lado de la identificación; el tratamiento del descifrado como análisis estático para leer código está en Deofuscación, sección 5.

Todas las cifras y listas de esta página vienen de la fuente del propio fabricante o de un estudio publicado, y las huellas concretas vienen de las reglas YARA de APKiD o de medición directa sobre el corpus. Lo que no se ha podido verificar va marcado.

2. Catálogo de transformaciones

Once transformaciones cubren prácticamente todo lo que se encuentra. Para cada una: qué hace, qué le hace al decompilador y qué huella deja.

2.1 Renombrado de identificadores

Lo que hace R8. Clases, campos, métodos y paquetes pasan a a, b, c. La variante agresiva mueve además todas las clases a un solo paquete —Netflix tiene 17.329 clases en un paquete llamado o— o al paquete raíz.

Efecto: el decompilador produce código correcto. El lector pierde toda la semántica de los nombres, que es la mitad de la comprensión. Huella: mediana de longitud de identificador de 1 a 3 (R8 y ProGuard, sección 10). Paquete raíz poblado.

Dos refinamientos que distinguen al producto de pago:

  • Sobrecarga por nombre (name overloading): reutilizar el mismo nombre para métodos con firmas distintas y para campos de tipos distintos. Es legal en el DEX, donde un method_id lleva class_idx, proto_idx y name_idx, pero convierte el índice en ambiguo para quien busca por nombre.
  • Alfabetos hostiles. El estudio de Wermke et al. lo documenta: «Allatori y DexGuard construyen sobre el alfabeto de ofuscación de nombres de ProGuard y añaden palabras reservadas de Windows (AUX, NUL)». Dong et al. observaron en malware nombres con caracteres Unicode (È, ô) y secuencias confusas (llllllll, oO00O0oo). La gramática de SimpleName del DEX es mucho más permisiva que la de Java (Formato DEX, sección 7), así que un ofuscador puede emitir nombres que ningún decompilador a Java puede reproducir como código compilable.

Ese último punto explica una huella muy concreta de APKiD: las reglas dexguard_b y dexguard_d buscan literalmente clases llamadas AUX, CON e IF en cualquier combinación de mayúsculas, y la de Lo/Lo/AUX;, Lo/CON;— la marca como específica de DexGuard porque otros ofuscadores usan esos nombres pero no dentro de un paquete o.

2.2 Cifrado de cadenas

Las constantes de texto se sustituyen por datos cifrados más una llamada a un descifrador que se ejecuta al vuelo. Es la transformación más rentable para el que ofusca, porque las cadenas literales son la principal vía de orientación de quien lee código ajeno: nombres de endpoints, claves de preferencias, mensajes de error, nombres de clases usados por reflexión.

Efecto: el decompilador produce b(c("Èk9@sg^W0~aa ÿ")) en vez de "Wrong Password" —el ejemplo es de la propia documentación de Zelix—. La búsqueda por texto deja de funcionar. Huella: un string_ids lleno de secuencias no imprimibles o de longitud uniforme, y uno o pocos métodos estáticos invocados desde cientos de sitios con argumentos constantes distintos.

El límite lo declara el propio fabricante. Zelix: «el cifrado de cadenas de Zelix KlassMaster no es, ni puede ser, fundamentalmente irreversible». Wermke et al. lo dicen igual: «hay que llegar a un compromiso entre la fuerza del cifrado y el impacto en rendimiento de descifrar. El descifrador tiene que estar dentro del programa, lo que hace el cifrado inadecuado para ocultar información sensible».

2.3 Cifrado de clases

El bytecode de clases enteras se guarda cifrado y se descifra y carga en ejecución. Es la frontera con el packing: cuando se aplica a todo el código, ya es un packer (Packers y protectores); aplicado selectivamente a unas pocas clases, es una transformación más del ofuscador.

Efecto: esas clases sencillamente no están en el DEX. El decompilador no las ve. Huella: ficheros de datos opacos y de alta entropía en assets/ o en la raíz del APK con nombres cortos, y un class_defs_size menor de lo que sugiere el tamaño del APK.

2.4 Aplanamiento y ofuscación del flujo de control

Se reestructuran los if/while/for en formas que no tienen equivalente directo en Java: un despachador con un switch sobre una variable de estado, saltos cruzados, predicados opacos cuyo valor es constante pero indecidible localmente.

Efecto: Zelix lo describe con precisión: «normalmente fuerza a los decompiladores a insertar una serie de etiquetas y sentencias goto ilegales en el código fuente que producen» y «a veces el código fuente se ofusca aún más por los errores del decompilador». Es decir: la salida ni siquiera compila. Huella: densidad anómala de goto/goto/16/goto/32 y de packed-switch/sparse-switch (Bytecode Dalvik); métodos con un solo bucle enorme y un switch sobre un registro que se reasigna en cada rama.

2.5 Inserción de código muerto y basura

Instrucciones y ramas que nunca se ejecutan, o que se ejecutan y no hacen nada.

Efecto: infla el volumen a leer y despista al análisis automático. Huella: nop en cantidad, aritmética sin consumidor, ramas cuya condición es constante. Obfuscapk implementa tres de estas de forma pura y documentada: Nop («inserta código basura; Nop inserta instrucciones aleatorias de no-operación»), ArithmeticBranch («inserta cómputos aritméticos e instrucciones de salto como código basura») y Goto («inserta una instrucción goto que apunta al final del método»).

2.6 Indirección por reflexión

Una llamada directa objeto.metodo(x) se sustituye por Class.forName(s1).getMethod(s2, …).invoke(objeto, x), casi siempre con s1 y s2 cifrados (sección 2.2).

Efecto: el grafo de llamadas se rompe. Dong et al. lo explican bien: «la reflexión es una buena opción para ocultar comportamientos del programa porque puede transferir el control a una función determinada de forma implícita, algo que las herramientas de análisis estático del estado del arte no manejan bien». Huella: proporción elevada de Ljava/lang/reflect/Method;.invoke y Class.forName sobre el total de invocaciones. Zelix la vende como Reference Obfuscation: «permite ofuscar las referencias a campos o métodos sustituyéndolas por llamadas a la Reflection API o por invokedynamic», y además «cifra los nombres de los campos, métodos y clases a los que se accede por esa vía».

Dato de escala, de Dong et al. sobre 114.560 aplicaciones: de 121.262 llamadas reflexivas observadas, 116.595 (96,2 %) tenían objetivo resoluble. Y su conclusión sobre prevalencia es menos alarmante de lo que suele decirse: «las proporciones de uso de reflexión en aplicaciones benignas y en malware son similares» y «la mayoría de los casos de reflexión se usan para invocar funciones ocultas o para dar soporte a compatibilidad hacia atrás».

2.7 Ofuscación aritmética

Expresiones aritméticas y lógicas simples se sustituyen por otras equivalentes pero enrevesadas (aritmética booleana mixta, identidades algebraicas). Guardsquare lo vende literalmente como protección de fórmulas: «DexGuard protege las fórmulas propietarias transformando expresiones aritméticas y lógicas simples en código difícil de analizar».

Efecto: el decompilador produce código correcto e incomprensible. Huella: densidad anómala de xor-int, and-int, shl-int y compañía. La regla dexguard_c de APKiD se apoya exactamente en eso: busca un método con la firma a(IIZI[I[[I[I)V que contenga xor-int/2addr, con el comentario «xor es bastante raro en código legítimo».

2.8 Ocultación de llamadas a la API

Caso particular de 2.6 dirigido a las llamadas que delatan. Guardsquare: «DexGuard añade reflexión para acceder a APIs sensibles, como las APIs estándar de Android para validación de firmas o para operaciones criptográficas».

Efecto: un análisis que busque PackageManager.getPackageInfo o javax.crypto no encuentra nada. Huella: ausencia sospechosa de llamadas a APIs que los permisos del manifiesto implican. Obfuscapk tiene una transformación específica, AdvancedReflection, descrita como «usa reflexión para invocar APIs peligrosas del framework de Android».

2.9 Ofuscación de recursos

Dos cosas distintas que se confunden:

  • Nombres de entrada en resources.arsc: el nombre simbólico (activity_main) se colapsa o se sustituye.
  • Rutas de fichero dentro del APK: res/layout/activity_main.xml pasa a res/a/b.xml o a res/raw/AW.xml.

Lo notable es que hay una vía oficial de Google para esto, en aapt2 optimize:

Opción de aapt2 optimize Qué hace (texto de la propia herramienta)
--collapse-resource-names «Colapsa los nombres de recurso a un único valor en el key string pool. Se pueden exceptuar recursos con la directiva no_collapse»
--shorten-resource-paths «Acorta las rutas de los recursos dentro del APK. Se pueden exceptuar con no_path_shorten»
--save-obfuscation-map <f> «Ruta donde volcar el mapa de rutas/nombres originales a ofuscados»

Ese último fichero es el mapping.txt de los recursos, y es lo que hace que esta transformación sea determinísticamente reversible si se tiene el mapa y prácticamente irreversible si no. Ver Deofuscación, sección 4.

Huella: medida directamente sobre el corpus, hay tres firmas distintas y bien separadas:

Firma en aapt2 dump resources Qué es Visto en
anim/0_resource_name_obfuscated aapt2 --collapse-resource-names. La cadena aparece una sola vez en el resources.arsc, porque todas las entradas apuntan al mismo índice del key string pool Snapchat (22.890 entradas)
anim/(name removed) Mismo mecanismo, cadena centinela distinta. También una sola aparición en el fichero WhatsApp (54.797 entradas)
anim/2130771970 El nombre se sustituye por el propio resource ID en decimal (0x7f010002). Hay tantas cadenas distintas como entradas Netflix, con DexGuard

La cadena 0_resource_name_obfuscated está dentro del binario de aapt2 37.0.0 de esta máquina, junto a no_collapse y a los descriptores protobuf aapt.pb.CollapsedNamesMap: es un artefacto oficial, no de un ofuscador de terceros.

2.10 Virtualización

La forma más fuerte. El cuerpo del método se traduce a un bytecode propio y se sustituye por una llamada a un intérprete de ese bytecode incluido en la aplicación. Guardsquare: «la virtualización de código transforma las implementaciones de los métodos en instrucciones para máquinas virtuales generadas aleatoriamente».

Efecto: no hay nada que decompilar. El analista tiene que reconstruir primero la máquina virtual y luego su juego de instrucciones, que es distinto en cada build. Huella: métodos enormes con un switch gigante sobre un array de bytes, y un array de datos que no es texto ni recurso.

2.11 Endurecimiento del código nativo

Mover lógica a .so y proteger ese .so. Guardsquare: «DexGuard endurece las bibliotecas nativas frente a la ingeniería inversa y la manipulación, incluida la interfaz entre las bibliotecas nativas y el código de la aplicación». Se trata en Anti-análisis y hardening, sección 7.

3. DexGuard (Guardsquare)

El hermano de pago de ProGuard, del mismo fabricante. Nació en 2012, dos años antes de que se fundara Guardsquare. Sede en Lovaina, Bélgica. Licencia comercial sin precio público; el estudio de Wermke et al. lo registra en su tabla comparativa como «On request», frente a los 290 $ de Allatori y los 800 $ de DexProtector.

Lo que dice de sí mismo su ficha de producto de 2024, verbatim y agrupado por el eje de este documento:

Categoría Técnicas declaradas
Ofuscación estática Name obfuscation (clases, campos, métodos, bibliotecas nativas, y también nombres de recursos, ficheros de recursos, ficheros de assets y atributos XML de recursos), control flow obfuscation, arithmetic obfuscation, call hiding, removal of Android logging code
Cifrado Data encryption: «cifra cadenas sensibles… También cifra clases, ficheros de assets, ficheros de recursos y bibliotecas nativas»
Virtualización Code virtualization
Nativo Native code obfuscation
RASP Certificate checks, debugger and emulator checks, root detection, hook detection, tamper detection, malware protection

Y la frase que explica por qué se lo encuentra uno tan a menudo: «DexGuard es compatible hacia atrás con ProGuard. Eso hace fácil la migración: puedes reutilizar tu configuración de ProGuard e implementar las capas adicionales de protección de DexGuard». Cubre además aplicaciones multiplataforma —Unity, Cordova, Ionic, Flutter, React Native— y declara «más de 900 clientes en todo el mundo».

⚠️ sin verificar: si DexGuard hace packing en el sentido de Packers y protectores. Guardsquare no usa esa palabra en ninguna de sus páginas consultadas; lo que sí declara es cifrado de clases, que es la transformación 2.3. La evidencia del corpus que sigue no respalda tratarlo como packer: el classes.dex de Netflix contiene código real decompilable, no un stub.

3.1 La evidencia real en el Netflix del corpus

com.netflix.mediaclient.sai.zip, base.apk, 21,9 MB, versionName 8.76.0 build 7 50451. Todo lo que sigue está medido directamente, no inferido de una regla de detección.

Cadenas y clases que nombran al producto, encontradas recorriendo string_ids y class_defs de los cinco classes*.dex:

DEX Cadena
classes.dex DEXGUARD, Dexguard
classes2.dex, classes5.dex Lcom/netflix/mediaclient/dexguard/impl/DexguardImpl$DexguardModule;
classes3.dex Lcom/netflix/mediaclient/service/player/streamingplayback/exosessionplayer/DexguardInitializerError;
classes3.dex Ignore dexguard error
classes3.dex SPY-37220 - dexguard unable to decrypt ASE

Las dos últimas son las más elocuentes: son mensajes de manejo de errores del propio Netflix ante fallos del descifrado de DexGuard, con hasta un identificador de incidencia interno. El producto está integrado como un módulo de inyección de dependencias (DexguardModule) del propio código de la aplicación.

Repaquetado agresivo. El manifiesto declara android:name="o.KY" como clase Application. De las 43.838 clases definidas en los cinco DEX, 17.329 están en un paquete llamado o —el 39,5 %—, mientras que el resto conserva su paquete real (com/netflix/…, androidx/…, com/google/android/gms/…, 1.337 paquetes distintos). Es decir: DexGuard mueve el código propio a un paquete de una letra y deja las bibliotecas de terceros donde estaban.

Cifrado de clases y de assets. En la raíz del APK, no en assets/, hay ocho ficheros con nombres de dos caracteres:

a-   9.329 bytes    b'java/lang/Integer' + 9.312 bytes de alta entropía
b-   1.153           java/lang/Integer  + …
c-   9.893           java/lang/Integer  + …
d-  46.969           java/lang/Integer  + …
e-   9.900           java/io/Serializable + …
f-  45.460           java/io/Serializable + …
g-  14.012           java/io/Serializable + …
h-  69.996           java/io/Serializable + …

Cada uno empieza por un descriptor de tipo Java en ASCII plano y sigue con datos opacos. Hay además un assets/e de 205.861 bytes sin cabecera reconocible y un assets/volicense_av1.dat de 135 bytes que tampoco es texto.

Ofuscación de recursos, completa. De las 4.656 entradas bajo res/, 4.655 están en res/raw/ con nombres de una a dos letras (A.xml, AW.xml, BA.xml…), y la restante en res/xml/. La estructura por tipo de recurso ha desaparecido del ZIP. Y en el resources.arsc, el nombre de cada entrada es su propio resource ID en decimal:

type anim id=01 entryCount=54
  resource 0x7f010002 anim/2130771970
    () (file) res/raw/f.xml type=XML

0x7f010002 = 2.130.771.970. Es una firma distinta de la de aapt2 (sección 2.9) y por sí sola distingue a DexGuard de una aplicación optimizada con las herramientas de Google.

Lo que DexGuard se llevó por delante. No hay ni un META-INF/*.kotlin_module en el APK, cuando aplicaciones ofuscadas con R8 a secas conservan decenas (sección 6.2). No hay marcador ~~R8 en ningún DEX; solo un ~~D8{"compilation-mode":"release","version":"1.6.68"} residual de alguna dependencia.

Y lo que no. La mediana de longitud de identificador del primer DEX de Netflix es 10, con solo un 22,0 % de identificadores de tres caracteres o menos. Sus primeras clases son Landroid/support/v4/media/MediaBrowserCompat$ConnectionCallback;, legibles. El paquete com/netflix/mediaclient/graphql/models/type/ conserva nombres como ArtworkAlignment y ArtworkFallbackStrategy. DexGuard está configurado selectivamente: repaquetado y cifrado sobre una parte del código, no sobre todo.

4. Allatori

De Smardec Inc. Versión 9.8, publicada el 1 de junio de 2026. Ofuscador de bytecode Java con soporte explícito de Android: se engancha en build.gradle con un doLast tras la compilación Java y antes de generar el DEX. Eso lo sitúa en un punto distinto de la cadena que R8, y explica que sus efectos y los de R8 se acumulen.

Su lista de características, verbatim: Name Obfuscation, Flow Obfuscation, Debug Info Obfuscation, String Encryption, 100% Protection Against Popular Decompilers, Optimizing, Watermarking, Incremental Obfuscation, Stack Trace Utility, Build Tool Interface, J2ME Obfuscation, Android Obfuscation.

Dos cosas que no tiene ningún otro de esta lista:

  • Marca de agua (watermarking): «cualquier cadena que se incruste en los jar de la aplicación. Puede ser el copyright, el nombre del cliente, el nombre de la empresa o cualquier otra información que identifique unívocamente el build». Es decir, Allatori añade información identificatoria a propósito, al contrario que el resto.
  • Caducidad (expiry): «se usa para poner fecha de caducidad a la aplicación. Las comprobaciones de caducidad se insertan en muchos métodos, no solo en main».

Su cifrado de cadenas tiene cuatro niveles (enable por defecto —solo cadenas seguras—, disable, maximum —todos los literales—, maximum-with-warnings) y dos tipos, fast y strong. El renombrado se configura por separado para paquetes, clases, métodos, campos y variables locales, con esquemas de nombres que incluyen carácter único, alfabético, numérico y palabras reservadas.

Licencias: 290 $ por desarrollador individual, 3.750 $ site, 4.850 $ business, y gratuito para proyectos educativos y no comerciales.

Huella: la regla allatori_demo de APKiD busca la cadena ALLATORIxDEMO en el DEX, que la versión de evaluación incrusta. Un build de pago no la lleva.

5. Zelix KlassMaster

De Zelix Pty Ltd, Australia. Se autodenomina «el primer ofuscador Java de segunda generación de verdad, de uso intensivo». Sus cinco funciones son Name Obfuscation, Flow Obfuscation, String Encryption, Reference Obfuscation y Trim.

Es el más útil de los cuatro como documentación, porque explica sus propios mecanismos con precisión inusual. Ya se le ha citado en las secciones 2.2, 2.4 y 2.6. Dos detalles adicionales:

  • Las variables locales no se renombran, se eliminan: «los nombres originales de las variables locales se han quitado». Es la misma operación que borrar el debug_info_item del DEX (Formato DEX, sección 12), y es irreversible por construcción.
  • Method Parameter Changes: añade parámetros extra a los métodos —su ejemplo muestra un long de más— para reforzar la ofuscación por referencia.

Trim es su reductor: «reduce el tamaño de la aplicación eliminando las clases, campos y métodos sin usar y los atributos de bytecode innecesarios», con análisis de las llamadas a la Reflection API que podrían ocultar usos legítimos. Es el equivalente del shrinking de R8.

Es un ofuscador de bytecode Java, no de DEX. Su propia página lo define así: «un ofuscador Java cambia el bytecode Java». La documentación cubre Java ME MIDlet, Ant, Maven y Gradle, pero no menciona Android ni DEX. ⚠️ sin verificar: la ausencia de mención no prueba la ausencia de soporte; no se ha comprobado si existe una vía específica para Android. En la práctica se lo encuentra uno en aplicaciones Android igualmente, porque cualquier ofuscador de .class funciona antes de D8.

Precios: 585 $ estándar, 290 $ para organizaciones de hasta dos personas, 2.195 $ site.

6. obfuscapk

Obfuscapk es lo contrario de los tres anteriores: académico, de código abierto (licencia MIT) y de caja negra —opera sobre el APK ya construido, sin código fuente—. Publicado por Simone Aonzo, Gabriel Claudiu Georgiu, Luca Verderame y Alessio Merlo en SoftwareX vol. 11 (2020), art. 100403.

Vale la pena por dos motivos. Uno, su catálogo está documentado y es reproducible, lo que lo convierte en el banco de pruebas natural para un deofuscador. Dos, la separación en categorías es una taxonomía útil por sí misma. Sus 21 transformaciones:

Categoría Transformaciones
Trivial NewAlignment, NewSignature, Rebuild
Rename ClassRename (incluido el manifiesto), FieldRename, MethodRename
Encryption AssetEncryption, ConstStringEncryption, LibEncryption, ResStringEncryption
Code AdvancedReflection, ArithmeticBranch, CallIndirection, DebugRemoval, Goto, MethodOverload, Nop, Reflection, Reorder
Resource RandomManifest
Other VirusTotal

CallIndirection («añade métodos envoltorio que invocan a los originales, modificando el flujo de control»), MethodOverload («explota la sobrecarga de Java creando métodos void nuevos con nombres idénticos») y Reorder («cambia el orden de los bloques básicos y reorganiza el código con instrucciones goto») son buenos ejemplos de transformaciones que un evaluador puede aplicar y medir.

Depende de apktool, apksigner, zipalign y Java. ⚠️ no ejecutado localmente: obfuscapk no está instalado y su dependencia apktool tampoco.

7. Tabla de huellas

Reunida a partir de las reglas YARA de APKiD (versión 3.1.0 de master), de la documentación de cada fabricante y de la medición directa sobre el corpus. Las huellas de APKiD se citan verbatim de los ficheros de reglas.

Transformación Cómo se ve en el binario Herramientas que la aplican
Renombrado Mediana de longitud de identificador 1–3; paquete raíz o de una letra poblado R8, DexGuard, Allatori, Zelix, obfuscapk
Renombrado hostil Clases AUX, CON, IF (reglas dexguard_b, dexguard_d); nombres con Unicode no ASCII DexGuard, Allatori
Cifrado de cadenas string_ids con secuencias no imprimibles; descifrador estático invocado desde cientos de sitios DexGuard, Allatori, Zelix, DexProtector, obfuscapk
Cifrado de clases/assets Ficheros opacos de alta entropía con nombre corto en la raíz o en assets/ DexGuard (a-h- en Netflix), DexProtector (assets/classes.dex.dat, assets/dp.mp3)
Flujo de control Densidad anómala de goto y switch; salida del decompilador que no compila DexGuard, Allatori, Zelix, obfuscapk (Goto, Reorder)
Código muerto nop en masa, aritmética sin consumidor obfuscapk (Nop, ArithmeticBranch)
Reflexión Proporción alta de Class.forName + Method.invoke con argumentos cifrados DexGuard (call hiding), Zelix (Reference Obfuscation), obfuscapk
Aritmética Método a(IIZI[I[[I[I)V con xor-int/2addr (regla dexguard_c) DexGuard
Recursos, nombres 0_resource_name_obfuscated (una sola vez en el pool) · (name removed) · <resourceID en decimal> aapt2 --collapse-resource-names · DexGuard
Recursos, rutas res/a/b.xml, res/0.xml planos · todo bajo res/raw/XX.xml aapt2 --shorten-resource-paths · DexGuard
Marca de agua Cadena ALLATORIxDEMO (regla allatori_demo) Allatori (versión demo)
Runtime DexGuard guardsquare/dexguard/runtime/, Ldexguard/util/TamperDetector;, Ldexguard/util/CertificateChecker;, com/guardsquare/dexguard/ (reglas dexguard_c, dexguard_d) DexGuard
Nativo DexGuard libdgrt.so, símbolo Java_com_guardsquare_dexguard_runtime_detection_HookDetector (reglas dexguard_native, dexguard_native_a) DexGuard 9.x
ELF falsificado Cabecera 7f 45 4c 46 … 44 50 4c 46\x7fELF seguido de DPLF (regla dexprotector) DexProtector
Arxan / Digital.ai Paquetes L(aaaaaa|bbbbbb|…)/[a-z]{6} y Lcom/arxan/guardit; fichero guardit4j.fin en la raíz Arxan / Digital.ai
Gemalto lib/<abi>/libmedl.so Gemalto

Nota metodológica sobre APKiD: es una herramienta de identificación por firma, con las limitaciones que eso implica. Su versión 3.1.0 declara 297 reglas —132 con etiqueta packer, 75 protector, 68 obfuscator, 14 compiler— y está bajo licencia dual GPL-3.0 y comercial. La firman Caleb Fenton y Tim Strazzere; el mayor contribuidor por número de commits es hoy Eduardo Novella. ⚠️ no ejecutado localmente: APKiD no está instalado. Las reglas se han leído directamente de los ficheros .yara del repositorio; las huellas que se marcan como «medidas sobre el corpus» se han comprobado con lectores propios, no con APKiD.

Y un resultado negativo que importa: ninguna de las huellas de packer del catálogo de APKiD aparece en el corpus. Buscadas por nombre de fichero y por contenido de los 323 classes*.dex de los 86 contenedores, no hay libjiagu, ni libsecexe, ni libDexHelper, ni libshell*.so, ni assets/ijiami.dat, ni pairip. Ver Packers y protectores, sección 7.

8. Qué NO se puede ofuscar

Esta sección es la más importante para quien construye herramientas, porque describe el suelo: lo que sigue siendo legible pase lo que pase, y sobre lo que se apoya todo el análisis de una aplicación protegida.

8.1 La superficie declarada en el manifiesto

Los android:name de activity, service, receiver y provider tienen que corresponder a clases que el framework pueda instanciar por nombre. Se pueden renombrar de forma coherente —el manifiesto se reescribe con el nombre nuevo, y eso es lo que hace R8, y lo que hace Netflix, cuya Application es o.KY— pero no pueden desaparecer ni dejar de corresponder. Consecuencia: el manifiesto siempre da la lista exacta de puntos de entrada, y por tanto el conjunto de clases desde las que arranca cualquier análisis.

Lo mismo vale para los intent-filter, los permisos, los meta-data y los provider autorities: son contrato con el sistema y con otras aplicaciones.

La formulación académica más precisa que se ha encontrado es de Wermke et al.: «las clases que necesitan ser accesibles desde un contexto externo… las clases que extienden clases nativas de Android como activities, services o content providers deberían quedar sin ofuscar en la mayoría de los casos para que la biblioteca o el sistema puedan invocar sus callbacks».

⚠️ sin verificar: no se ha encontrado ninguna página oficial de Google que afirme literalmente que AGP genera reglas -keep automáticas a partir del manifiesto. Lo confirmado es la afirmación funcional: los componentes del manifiesto son entry points del grafo de R8.

8.2 Los puntos de entrada del framework

Todo método que el framework llama por nombre a través de una interfaz o de una superclase conserva ese nombre: onCreate, onBind, onReceive, run, compare, test. No es una concesión del ofuscador, es que el nombre está en la superclase, que vive fuera de la aplicación. Renombrar el método de la subclase rompería el override.

De ahí que la métrica de la sección 10 de R8 y ProGuard nunca llegue al 100 %: incluso en la aplicación más ofuscada del corpus queda un 8 % de métodos con nombre largo, y son estos.

Las reglas por defecto de AGP amplían ese suelo de forma bien documentada (R8 y ProGuard, sección 5.2): sobreviven con su nombre los set*/get* de las subclases de View, los values()/valueOf() de los enum, el campo CREATOR de todo Parcelable y los métodos anotados con @JavascriptInterface.

8.3 Las llamadas a la API de Android

android.*, java.*, javax.* y androidx.* en su parte no reempaquetada no son parte de la aplicación: son referencias externas resueltas en tiempo de carga. Aparecen en type_ids y method_ids con su nombre real, siempre. Ocultarlas exige la indirección por reflexión de la sección 2.6, que cambia el problema —hay que resolver la cadena— pero no lo elimina, y que además deja su propia huella.

⚠️ sin verificar: no se ha encontrado una cita oficial que enuncie esto de forma literal. El mecanismo indirecto sí está documentado: en ProGuard las bibliotecas se pasan con -libraryjars y se referencian sin procesarse.

8.4 Los métodos nativos

La regla estándar que AGP incluye por defecto es explícita:

-keepclasseswithmembernames,includedescriptorclasses class * { native <methods>; }

con la justificación de Guardsquare: «conservar sus nombres y los de sus clases, para que se puedan seguir enlazando con la biblioteca nativa». La documentación de JNI de Android da la alternativa: registrar los métodos dinámicamente con RegisterNatives() desde JNI_OnLoad(), en cuyo caso «el único símbolo que normalmente hay que exportar es JNI_OnLoad()». Un ofuscador serio usa esa segunda vía, y entonces el nombre del método Java sí se puede renombrar — pero la correspondencia sigue existiendo dentro de JNI_OnLoad, en el binario nativo.

8.5 Lo que sobrevive por descuido

Cosas que no son estructuralmente irrenombrables pero que en la práctica nadie renombra:

  • META-INF/*.kotlin_module. Conservan el nombre del módulo Gradle. En Shazam, con un 70 % de identificadores cortos, siguen ahí AMSKit_release.kotlin_module y app_googleRelease.kotlin_module; en Reddit, account_impl.kotlin_module, awards_public.kotlin_module, deeplinkdispatch_release.kotlin_module. Es el organigrama del proyecto, intacto. DexGuard sí los elimina: Netflix no tiene ninguno.
  • META-INF/*.version y *.properties. Netflix lleva 98 ficheros androidx.<artefacto>.version y 30 play-services-*.properties en la raíz, cada uno con el nombre y la versión de una dependencia. Es un SBOM completo entregado gratis.
  • Los mensajes de error y de telemetría propios. SPY-37220 - dexguard unable to decrypt ASE no debería estar ahí.
  • Los nombres de recursos referenciados desde el manifiesto, que tienen que resolverse.

Ese suelo es lo que hace posible el análisis pese a todo, y es la base de las heurísticas de Deofuscación, sección 5.

9. Cuánta ofuscación hay realmente

Dos estudios grandes, con cifras que conviene no confundir.

Wermke et al., ACSAC 2018, «A Large Scale Investigation of Obfuscation Use in Google Play», sobre 1.762.868 aplicaciones gratuitas de Google Play:

  • «solo el 24,92 % de las aplicaciones están ofuscadas por el desarrollador».
  • «Aproximadamente el 25 % de las aplicaciones están ofuscadas, pero ese número sube al 50 % para las más populares, con más de 10 millones de descargas».
  • Pero el detector encontró el patrón de renombrado de la familia ProGuard en 1.137.228 (64,51 %). La diferencia con el 24,92 % es la clave: «un gran porcentaje de aplicaciones no fueron ofuscadas intencionadamente por el desarrollador original, sino que incluían bibliotecas de terceros que usaban ofuscación».
  • «ProGuard es, con diferencia, la herramienta de ofuscación más popular para Android».
  • Y un resultado sobre el factor humano: «el 78 % falló al usar ProGuard correctamente en un escenario más complejo y realista».

Dong et al., SecureComm 2018, «Understanding Android Obfuscation Techniques», sobre 114.560 aplicaciones (26.614 de Google Play, 65.666 de mercados chinos, 22.280 de malware), recogidas entre 2016 y 2017:

Técnica Google Play Mercados de terceros Malware
Renombrado de identificadores 57,0 % 73,0 % 63,5 %
Cifrado de cadenas 0,0 % 0,1 % 5,3 %

La segunda fila es el dato que más contradice la intuición: el cifrado de cadenas es prácticamente inexistente en aplicaciones legítimas. La explicación que dan los autores es económica: «el cifrado de cadenas no es una característica habitual de los ofuscadores de estantería (ProGuard). Los ofuscadores que ofrecen cifrado de cadenas son caros (DexGuard, DexProtector)».

Consecuencia para quien construye herramientas: un descifrador de cadenas sirve para analizar malware y aplicaciones de alto valor, no para el catálogo general. Y el renombrado, que sí es masivo, es exactamente el problema que mapping.txt resuelve cuando está y que hay que inferir cuando no (Deofuscación).

Los datos del corpus son coherentes con esos rangos. De las 22 aplicaciones comerciales medidas en detalle en R8 y ProGuard, sección 10.2, 15 tienen renombrado de identificadores —mediana de longitud de 1 a 3— y 7 no lo tienen —Telegram, Netflix, Clash Royale, Coin Master, Signal, LinkedIn Learning y Candy Crush, con medianas de 10 a 17—, aunque varias de estas últimas sí pasaron por R8 con shrinking. Y solo una, Netflix, tiene cifrado de cadenas y de clases. Aplicar un ofuscador comercial es la excepción, no la norma.

Fuentes

  1. DexGuard factsheet 2024 (Guardsquare) — https://www.guardsquare.com/hubfs/Website/Resources/Fact%20sheets/factsheet-DexGuard-2024.pdf Consultado el 12 de agosto de 2026. De aquí sale íntegra la tabla de técnicas declaradas de la sección 3, las citas de las secciones 2.1, 2.7, 2.8, 2.10 y 2.11, y la compatibilidad hacia atrás con ProGuard.
  2. DexGuard (página de producto) — https://www.guardsquare.com/dexguard Consultado el 12 de agosto de 2026. De aquí sale que no hay precio público y el enfoque de capas superpuestas y polimorfismo de la sección 3.
  3. ProGuard manual — Configuration examples — https://www.guardsquare.com/manual/configuration/examples Consultado el 12 de agosto de 2026. De aquí sale la regla estándar de métodos nativos y su justificación (sección 8.4).
  4. Allatori — Features / Docs / Price — http://www.allatori.com/features.html, http://www.allatori.com/doc.html, http://www.allatori.com/price.html Consultados el 12 de agosto de 2026. De aquí salen la lista de características, el watermarking, el expiry, los niveles de cifrado de cadenas, la integración en Gradle antes del DEX y los precios de la sección 4.
  5. Zelix KlassMaster — Features y subpáginas — https://www.zelix.com/klassmaster/features.html y las páginas featuresNameObfuscation.html, featuresFlowObfuscation.html, featuresStringEncryption.html, featuresReferenceObfuscation.html, featuresTrim.html Consultadas el 12 de agosto de 2026. De aquí salen las citas de las secciones 2.2, 2.4, 2.6 y toda la sección 5, incluida la admisión de que su cifrado de cadenas no es irreversible.
  6. Obfuscapk — https://github.com/ClaudiuGeorgiu/Obfuscapk Consultado el 12 de agosto de 2026. De aquí sale la lista completa de 21 transformaciones con sus descripciones (sección 6) y la cita del paper de SoftwareX. ⚠️ https://www.sciencedirect.com/science/article/pii/S2352711020300108 devolvió HTTP 403; la referencia bibliográfica procede del README del repositorio.
  7. Wermke, Huaman, Acar, Reaves, Traynor, Fahl — «A Large Scale Investigation of Obfuscation Use in Google Play», ACSAC 2018 — https://arxiv.org/pdf/1801.02742 Consultado el 12 de agosto de 2026. De aquí salen las cifras de la sección 9, la nota sobre AUX/NUL de la sección 2.1, la tabla de licencias citada en la sección 3 y la formulación sobre lo que no puede ofuscarse de la sección 8.1. ⚠️ https://dl.acm.org/doi/10.1145/3274694.3274726 devolvió HTTP 403; se ha usado la versión de arXiv.
  8. Dong, Li, Diao, Liu, Liu, Li, Xu, Chen, Wang, Zhang — «Understanding Android Obfuscation Techniques: A Large-Scale Investigation in the Wild», SecureComm 2018 — https://diaowenrui.github.io/paper/securecomm18-dong.pdf Consultado el 12 de agosto de 2026. De aquí salen las dos tablas de prevalencia de la sección 9, las citas sobre reflexión de la sección 2.6 y la observación sobre alfabetos de renombrado de la sección 2.1.
  9. APKiD — reglas YARA — https://github.com/rednaga/APKiD, ficheros apkid/rules/apk/obfuscators.yara, apk/packers.yara, dex/obfuscators.yara, dex/packers.yara, elf/obfuscators.yara, elf/packers.yara, leídos en crudo desde raw.githubusercontent.com/rednaga/APKiD/master/ Consultados el 12 de agosto de 2026. De aquí salen todas las huellas de la tabla de la sección 7 marcadas con nombre de regla, y los datos del proyecto (versión 3.1.0, licencia dual, autoría, recuento de reglas).
  10. aapt2 optimize --help, build-tools 37.0.0 — ejecutado el 12 de agosto de 2026. De aquí salen --collapse-resource-names, --shorten-resource-paths, --save-obfuscation-map y las directivas no_collapse/no_path_shorten de la sección 2.9.
  11. JNI tips — https://developer.android.com/training/articles/perf-jni Consultado el 12 de agosto de 2026. De aquí sale RegisterNatives() desde JNI_OnLoad() y la advertencia sobre keep rules de la sección 8.4.
  12. Mediciones propias sobre el corpus — ejecutadas el 12 de agosto de 2026 sobre ~/corpus-apk con build-tools 37.0.0 (aapt2 37.0.0) y Python 3.12. De aquí sale toda la sección 3.1 —cadenas de DexGuard, distribución de paquetes, ficheros a-h-, estructura de res/, volcado de resources.arsc—, las tres firmas de recursos de la sección 2.9, la ausencia de huellas de packer de la sección 7 y las observaciones sobre kotlin_module de la sección 8.5.