Packers y protectores
Antes conviene leer «Formato DEX», «Ofuscadores comerciales»
1. Alcance de este documento, antes de nada
Este documento explica el modelo de funcionamiento de un packer y las huellas que permiten
identificarlo. No explica cómo extraer el payload, ni cómo volcar el DEX descifrado de
memoria, ni cómo instrumentar el cargador. Es la línea que se mantiene en toda la
documentación, y la «sección 9 · Por qué desempaquetar está fuera del alcance» explica el
razonamiento económico y legal que hay detrás en vez de dejarlo como una prohibición sin
argumento.
La consecuencia práctica: aquí hay nombres de fichero, nombres de clase, cadenas y estructuras que sirven para contestar la pregunta «¿qué me he encontrado?». Buena parte de la literatura académica que se cita describe además cómo desempaquetar; de esos trabajos se ha extraído únicamente el modelo y las huellas.
Versiones de referencia: APKiD 3.1.0 y su rama master, y build-tools 37.0.0, consultadas en
agosto y septiembre de 2026.
2. Qué es un packer y por qué existe
Un ofuscador («Ofuscadores comerciales») transforma el código
para que sea difícil de leer. Un packer hace algo distinto y más radical: quita el
código del APK. Lo cifra, lo guarda como datos, y pone en su lugar un classes.dex
mínimo cuyo único trabajo es descifrarlo y cargarlo en tiempo de ejecución.
Duan et al. lo enuncian de forma compacta: «una aplicación Android empaquetada suele empaquetar su código Java, y también su código nativo, dentro de ficheros de recurso binarios. Normalmente mantiene además un componente Java ficticio, que actúa únicamente como despachador para lanzar el procedimiento de desempaquetado».
Existe por dos motivos que conviene separar. El legítimo: proteger propiedad intelectual —la lógica de un juego, un algoritmo de recomendación, un DRM— frente a la copia y el reempaquetado. El otro: el malware lo usa para evadir el análisis estático de los antivirus, y de hecho lo usa mucho más que las aplicaciones legítimas («sección 6 · Prevalencia real»).
Para el analista, la diferencia con un ofuscador es cualitativa. Con un ofuscador queda
código que decompilar, aunque sea horrible. Con un packer, el decompilador abre el APK y
encuentra doscientas clases de infraestructura. Todo lo que le interesa está en un fichero
de assets/ que es ruido.
3. El modelo común
Casi todos los packers implementan la misma arquitectura, con variaciones en el cifrado, en el momento de la carga y en el nivel de granularidad.
APK empaquetado
┌──────────────────────────────────────────────────────────────────┐
│ AndroidManifest.xml │
│ <application android:name="com.qihoo.util.StubApplication"/> │ ①
├──────────────────────────────────────────────────────────────────┤
│ classes.dex ← el STUB. Pocas clases, ninguna de la app real │ ②
├──────────────────────────────────────────────────────────────────┤
│ assets/ijiami.dat · assets/classes0.jar · assets/sealed1.dex │ ③
│ ← el DEX real, cifrado │
├──────────────────────────────────────────────────────────────────┤
│ lib/arm64-v8a/libjiagu.so · libDexHelper.so · libshella.so │ ④
│ ← el descifrador y el cargador, en nativo │
└──────────────────────────────────────────────────────────────────┘
Y su secuencia de arranque:
Zygote crea el proceso
│
▼
① Instancia la clase declarada en android:name ─── la del packer, no la de la app
│
▼
② Application.attachBaseContext(Context) ─── el gancho: se ejecuta ANTES de onCreate
│
├──► System.loadLibrary("jiagu") ─── ④ carga el descifrador nativo
│ │
│ ├──► lee ③, descifra el DEX real
│ └──► lo entrega al runtime
│
├──► DexClassLoader / InMemoryDexClassLoader
│ o manipulación directa de estructuras de ART
│
└──► por reflexión, sustituye el ClassLoader del proceso
y restaura la Application original de la app
│
▼
③ A partir de aquí la aplicación se ejecuta como si nada
3.1 El gancho: attachBaseContext
Es el detalle técnico que hace posible todo lo demás, y merece su propia explicación porque es donde converge toda la literatura.
Application hereda de ContextWrapper un método protected void attachBaseContext(Context base),
disponible desde el nivel de API 1. La documentación de Application da la otra mitad: «puedes
proporcionar tu propia implementación creando una subclase y especificando el nombre
completamente cualificado de esa subclase como atributo android:name en la etiqueta
<application> de tu AndroidManifest.xml. La clase Application… se instancia antes que
cualquier otra clase cuando se crea el proceso de tu aplicación/paquete».
Duan et al. lo señalan como el punto canónico: «AttachBaseContext() es la función que los
packers suelen sobrescribir para realizar estas tareas, ya que el framework la llama
incluso antes que OnCreate() y tiene el momento ideal para el preprocesado».
DexHunter añade el matiz que explica una huella concreta: las clases envoltorio
com.bangcle.protect.ApplicationWrapper (Bangcle) y com.shell.SuperApplication (Ijiami)
«solo aparecen si el fichero dex original tenía una clase Application» — dos packers
distintos con el mismo truco. Es decir, el packer sustituye la
Application del desarrollador y, si existía, la reintroduce después. Un manifiesto que
declare una Application con un nombre de paquete que no tiene nada que ver con el de la
aplicación es la señal más barata que existe.
3.2 Las tres vías de carga
| Vía | API | Qué implica |
|---|---|---|
DexClassLoader |
Nivel 3 | «Carga clases de ficheros .jar y .apk que contengan una entrada classes.dex. Se puede usar para ejecutar código no instalado como parte de una aplicación». Deja el DEX descifrado en disco |
InMemoryDexClassLoader |
Nivel 26 | «Carga clases desde un buffer que contiene un fichero DEX. Se puede usar para ejecutar código que no se ha escrito en el sistema de ficheros local» |
| Manipulación directa de ART | — | El nativo modifica las estructuras internas del runtime. La más difícil de observar |
Corrección frecuente: DexClassLoader no está obsoleta. Lo que está obsoleto es su
parámetro optimizedDirectory, que «está deprecado y no tiene efecto desde el nivel de API
26». La clase sigue documentada y sin aviso de deprecación, con la advertencia de siempre:
«No caches clases optimizadas en almacenamiento externo. El almacenamiento externo no
proporciona los controles de acceso necesarios para proteger tu aplicación frente a ataques
de inyección de código».
Los constructores de InMemoryDexClassLoader llegaron en tres oleadas, y el dato sirve para
acotar la edad de un packer: el de un solo ByteBuffer en API 26, el de array de buffers en
API 27, y el que además acepta librarySearchPath en API 29.
La tercera vía, la manipulación de estructuras, la documentan los trabajos de
desempaquetado. Happer describe que «los packers enganchan ClassLinker::LoadMethod para
modificar los objetos ArtMethod», y DexHunter que se «modifican estructuras de datos
clave en memoria, como DexHeader, ClassDef y ArtMethod». Las funciones nativas del
runtime que aparecen citadas son openDexFileNative(), openDexFile_bytearray() y
DexFile::<init>.
3.3 Tres niveles de sofisticación
La literatura describe tres regímenes claramente distintos, y la numeración en generaciones sí aparece de forma literal: Gupacker (Zheng et al., IEEE Transactions on Information Forensics and Security, vol. 20, 2025) habla de «first-generation holistic packer», «second-generation function extraction packers» y «third-generation virtual obfuscation packer», que se corresponden a grandes rasgos con los tres niveles de abajo; su segunda generación, la extracción de funciones, es una forma concreta del nivel 2, no todo él. Aquí se usan niveles, y cada uno se ancla a la fuente que lo describe.
Nivel 1 — liberación completa. Todo el DEX original se descifra de una vez antes de que
el flujo de control llegue a él. AppSpear lo llama full-code releasing: «descifra el fichero
DEX cifrado entero antes de que el flujo de control llegue a él». Parema lo formula como
regla: «los mecanismos tradicionales de empaquetado de aplicaciones siguen una regla de
escribir-y-luego-ejecutar».
Nivel 2 — descifrado incremental y por capas. AppSpear: «incremental code releasing, que descifra selectivamente solo la porción de código que se ejecuta realmente, y puede volver a cifrarla después de ejecutarla». El packer de Baidu, según el mismo trabajo, usa «cifrado en dos capas». Duan et al. lo miden: «estrategia de desempaquetado multicapa… capa a capa durante la ejecución», con Bangcle en 9 capas, ijiami en 4, Qihoo en 4 y Tencent en 40.
Nivel 3 — virtualización y traslado a nativo. Parema: «los packers de Android más recientes adoptan protecciones basadas en máquina virtual que nunca liberan el DCode… traducen el DCode a otro tipo de bytecode personalizado, denominado PCode, e incrustan una máquina virtual personalizada (P-VM)». Happer describe la variante complementaria: «JNI Transformation… los packers pueden usar código nativo para reimplementar métodos seleccionados de la aplicación».
Ese tercer nivel es la razón de que el desempaquetado genérico haya dejado de ser un problema
resoluble de una vez: no hay ningún momento en el que el DEX original exista.
4. Las familias
Origen, mercado y qué las distingue. El estado de cada sitio web se ha comprobado directamente el 12 de agosto de 2026 y se declara, porque buena parte de este ecosistema es chino y sus sitios entran y salen.
| Familia | Vendedor / origen | Estado del sitio | Qué la distingue |
|---|---|---|---|
| Jiagu / 加固保 | Qihoo 360 (Beijing, China), plataforma «360 Tianyu» | jiagu.360.cn responde, pero es una SPA vacía sin contenido legible |
El packer chino más extendido. Su .so incluye una función señuelo llamada youAreFooled |
| Bangcle | Bangcle (China) | ❌ bangcle.com devuelve HTTP 500 |
Uno de los más antiguos. 9 capas de descifrado según Duan et al. |
| SecNeo | SecNeo (China) | ❌ secneo.com NXDOMAIN |
Tres variantes (A, B, C) distinguibles por su biblioteca nativa |
| Tencent Legu / 乐固 | Tencent (China) | ❌ portal de producto inaccesible | El más agresivo por capas: 40 según Duan et al. Assets con nombres deliberadamente confusos (0OO00l111l1l) |
| Virbox Protector | Beijing Shendun Technology (Pekín, China) | ✅ shell.virbox.com |
Cubre nativo, Java, .NET, Python, ARM Linux. Ofrece shell y virtualización |
| DexProtector | Licel | ✅ dexprotector.com, licelus.com |
«Cifra de forma segura cadenas, clases enteras, assets y recursos». Motor InvokeDynamic. Falsifica la cabecera ELF de sus bibliotecas |
| Ijiami / 爱加密 | Beijing Zhiyou Wang'an (Pekín, China) | ⚠️ ijiami.cn sí, www.ijiami.cn no resuelve |
Declara «tecnología de endurecimiento de sexta generación» y «más de un millón de apps» |
| Baidu | Baidu (China) | ✅ apkprotect.baidu.com |
Declara «6W+ apps endurecidas» y cobertura de AAB |
| AppSealing | Rebautizado como DoveRunner | ⚠️ redirige; doverunner.com devuelve 403 |
Producto AppSecurity. Marca sus assets con un directorio propio |
| Promon Shield | Promon AS, Oslo, Noruega, fundada en 2005 por Tom Lysemose | ✅ promon.io |
«RASP, anti-tamper, anti-repackaging y protección de código de primer nivel, todo integrado en profundidad post-compilación» |
| DexGuard | Guardsquare, Lovaina, Bélgica | ✅ | Cifra clases selectivamente. Ver la nota de abajo |
| Alibaba / Aliyun | Alibaba (China) | ❌ producto retirado o movido: 404 y NXDOMAIN | Su huella (libmobisec.so) sigue en las reglas de APKiD |
| APKProtect / Naga | — | — | Varias generaciones con huellas muy distintas |
| KiwiSec | KiwiSec | — | Bibliotecas libkiwi* |
Sobre DexGuard en modo packing. Se lo cataloga a veces como «un packer comercial», y el
fabricante lo respalda solo en parte: su ficha de 2024 habla de cifrado de clases, pero al
presentar DexGuard 8.1, el 30 de noviembre de 2017, anunció un «Code packing» que, además de
cifrar clases concretas, puede «encrypt all combined bytecode as an additional layer of
protection». El Netflix del «muestrario» no lo usa así:
su classes.dex contiene 8.920 clases reales y decompilables, con androidx y
com.google.android.gms legibles («Ofuscadores comerciales → §3.1 · La evidencia real en el Netflix del muestrario»). Lo que sí tiene son ocho ficheros cifrados en la raíz. Es cifrado selectivo de
clases, la transformación 2.3 de ese documento, no sustitución del DEX por un stub. Si el
cifrado de todo el bytecode sigue existiendo con ese nombre, las páginas públicas actuales de
Guardsquare no lo dicen.
5. Huellas de detección
Esta es la sección central. Todo lo que sigue está transcrito de las reglas YARA de APKiD
3.1.0, leídas en crudo del repositorio, salvo lo marcado como procedente de otra fuente. La
3.1.0, del 9 de abril de 2026, sigue siendo la última versión publicada en septiembre de 2026,
y en master las huellas de la tabla 5.1 son las mismas; master solo añade reglas ELF
nuevas para DexProtector (dexprotector_b).
APKiD es la mejor documentación de huellas que existe: 291 reglas en las cuatro etiquetas
relevantes, de las cuales 136 con etiqueta packer y 72 con etiqueta protector, bajo licencia
dual GPL-3.0 y comercial (el recuento, en
«Ofuscadores comerciales → §7 · Tabla de huellas»).
5.1 Por familia
| Familia | assets/ y raíz |
lib/ |
Clases DEX y cadenas |
|---|---|---|---|
| Jiagu / Qihoo 360 | assets/jiagu_data.bin, assets/sign.bin |
libjiagu.so, libjiagu_art.so, libapktoolplus_jiagu.so, libprotectClass.so |
En la .so: JIAGU_APP_NAME, JIAGU_SO_BASE_NAME, JIAGU_ENCRYPTED_DEX_NAME, JIAGU_HASH_FILE_NAME. Clase com.qihoo.util.StubApplication |
| Bangcle | assets/bangcleplugin/container.dex, bangcleclasses.jar, bangcle_classes.jar, assets/secData0.jar |
libsecexe.so, libsecmain.so, libSecShell.so, libSecShell-x86.so |
com.bangcle.protect.ApplicationWrapper, com.bangcle.protect.Acall, com.bangcle.protect.MyClassLoader, neo.proxy.DistributeReceiver |
| SecNeo | assets/classes0.jar |
libDexHelper.so, libDexHelper-x86.so; variante B libdexjni.so, variante C libdatajar.so |
— |
| Tencent Legu | assets/tosversion, assets/0OO00l111l1l, assets/0OO00oo01l1l, assets/o0oooOO0ooOo.dat; variante VMP assets/wsDal.jar, assets/WSSEC[A-D].jar |
libshell.so, libshella-<v>.so, libshellx-<v>.so, libmobisecy.so, libshell-superv*.<año>.so, libxgVipSecurity.so |
Lcom/tencent/StubShell/TxAppEntry;, Lcom/tencent/StubShell/a, com.tencent.StubShell.ProxyShell. Ruta /mix.dex |
| Virbox | — | libv++.so, libv++_64.so, libsandhook.so, libsandhook-native.so |
En la ELF, la cadena Virbox Protector. Clase Lvirbox/StubApp; |
| DexProtector | assets/classes.dex.dat, assets/classes<n>.dex.dat, assets/resources.dat, assets/dp.mp3, assets/dp.<abi>.so.dat |
libdexprotector.<algo>.so, libalice.so |
Cabecera ELF falsificada: DPLF en el offset 8 del e_ident |
| Ijiami | assets/ijiami.dat, ijiami.ajm/ijiami3.ajm, assets/ijm_lib/, assets/IJMDal.Data |
assets/libijmDataEncryption.so, libexec.so, libexecmain.so |
Stub UPX modificado: la magia UPX! sustituida por AJM!. Clases com.shell.NativeApplication, com.shell.SuperApplication |
| Baidu | baiduprotect1.jar, assets/baiduprotect.jar |
libbaiduprotect.so |
com.baidu.protect.StubApplication |
| Alibaba | — | libmobisec.so |
com.ali.mobisecenhance.StubApplication |
| AppSealing | assets/appsealing.dex, assets/sealed1.dex, assets/AppSealing/* |
libcovault.so, libcovault-appsec.so |
Lcom/inka/appsealing/AppSealingApplication;, AppSealingLoader … v1.2.2, APPSEALING-CORE-VERSION_2.10.10 |
| Promon Shield | — | libshield.so o un nombre aleatorio lib[a-z]{10,12}.so |
Secciones ELF anómalas .ncc (código), .ncd (datos), .ncu |
| KiwiSec / Kiro | assets/sbox |
libkiroro.so, libkiwicrash.so, libkiwi_dumper.so, libKwProtectSDK.so, libkadp.so, libwhite-box.so |
Lcom/kiwisec/crash/CrashUtils;, Lcom/kiwivm/security/StubApplication; |
| APKProtect | assets/apkprotect.bin, assets/apkprotect/classes.dex.bin, apkprotect-build.properties, META-INF/APKPROTECT.RSA |
libapkprotect.so, libAPKProtect.so |
Ruta apkprotect.com/key.dat |
| Naga | assets/maindata/fake_classes.dex |
libedog.so/libddog.so/libfdog.so/libvdog.so, libchaosvmp.so, libxloader.so |
— |
| Otros stubs | — | — | Lcom/manxi/shell/MXApplication; (Manxi), Lcom/merry/wapper/WapperApplication; (PangXie), Lcom/vdog/VDogApplication; (Crazy Dog), Lcom/inca/security/Proxy/JNISoxProxy; (AppGuard), Lcom/security/inner/stub000/x; (DingXiang), Lcom/nesun/stub/ZAP; (Nesun), Lweb/apache/sax/app; (Medusah AppSolid) |
5.2 Huellas transversales, que valen para familias que aún no tienen regla
Las anteriores envejecen: cambian con cada versión del producto. Estas no.
El stub UPX modificado. Tres familias comprimen su biblioteca nativa con UPX y sustituyen
la magia UPX! para que upx -d no la reconozca:
| Sustituto | Familia |
|---|---|
SEC! |
Bangcle y SecNeo |
\x03\x02\x01\x00 |
Bangcle y SecNeo, versiones nuevas |
AJM! |
Ijiami |
La regla busca el marcador dos veces: en los primeros 200 bytes y en los últimos 50 del fichero.
La secuencia de arranque en el bytecode. La regla apkguard_dex de APKiD no busca
nombres: busca la forma del código del stub, con los opcodes anotados uno a uno.
invoke-super {v14, v15}, Landroid/app/Application;.attachBaseContext:(Landroid/content/Context;)V
invoke-virtual {v14}, …getClassLoader:()Ljava/lang/ClassLoader;
new-instance v4, Ldalvik/system/DexClassLoader;
invoke-direct {…}, Ldalvik/system/DexClassLoader;.<init>:(String;String;String;ClassLoader;)V
invoke-virtual {v4, v9}, Ldalvik/system/DexClassLoader;.loadClass:(String;)Ljava/lang/Class;
const-string v10, "attachBaseContext"
invoke-virtual {…}, Ljava/lang/Class;.getDeclaredMethod:(…)
invoke-virtual {…}, Ljava/lang/reflect/Method;.invoke:(…)
Ese patrón —attachBaseContext → DexClassLoader → getDeclaredMethod("attachBaseContext")
→ invoke— es el packer, con independencia de quién lo haya escrito. Es la huella más
duradera que hay, y es genérica.
Anomalías estructurales del DEX. La regla jiagu_k de APKiD comprueba
(dex.header.data_size + dex.header.data_offset) < dex.header.file_size: hay bytes en el
fichero que la cabecera no declara. Es la clase de comprobación que solo se puede hacer
leyendo el fichero por el map_list y no por la cabecera
(«Formato DEX → §5 · map_list: la vía fiable de recorrer el fichero»). En la misma línea: un
class_defs_size de dos cifras en un APK de 30 MB, un assets/ con un fichero de varios
megabytes de entropía uniforme, o un lib/ con una biblioteca cuyo nombre no aparece en
ningún System.loadLibrary visible.
Cabeceras falsificadas. DexProtector escribe DPLF —presumiblemente «DexProtector
Linkable Format»— en el offset 8 del e_ident, donde deberían ir EI_ABIVERSION y los
tres primeros bytes de EI_PAD. El comentario de la propia regla incluye el volcado:
0x00000000 7f45 4c46 0101 0100 4450 4c46 0000 0000 .ELF....DPLF.... // armeabi-v7a
0x00000000 7f45 4c46 0201 0100 4450 4c46 00e0 0100 .ELF....DPLF.... // Aarch64
Contando desde cero: 7f 45 4c 46 son los offsets 0-3, y los bytes 4 a 7 —EI_CLASS,
EI_DATA, EI_VERSION y EI_OSABI— sí son válidos; 44 50 4c 46 empieza en el 8. La
regla lo hace explícito, anclada al principio del fichero y consumiendo esos ocho bytes antes
de la marca:
$dp_elf_header = { 7f45 4c46 (01|02) 01 0100 4450 4c46 }
5.3 Corroboración independiente
Las reglas de APKiD son firmas; conviene comprobar que describen algo real. El análisis de
Romain Thomas sobre Legu (Quarkslab, 26 de noviembre de 2019) nombra los mismos cuatro
assets: «el packer usa los primeros 16 bytes de assets/tosversion xoreados con una clave
fija», y enumera «tosversion, 0OO00l111l1l, 0OO00oo01l1l, o0oooOO0ooOo.dat». Coinciden
exactamente con la regla tencent_legu.
⚠️ En cambio los nombres de biblioteca de aquel sample —orange-super.2019.so,
orangea-4.1.0.XY.so— no coinciden con los libshell*.so de las reglas. Es el
recordatorio de siempre: las huellas por nombre de fichero envejecen y varían entre versiones
del mismo producto.
6. Prevalencia real
6.1 El 0,59 %
Se afirma con frecuencia que «solo el 0,59 % de las aplicaciones de Google Play llevan packer detectable, frente al 40-43 % en los markets chinos». La fuente existe y se ha localizado.
La frase original, literal:
«Compare that to Google Play at 0.59% packed and 2.65% obfuscated, an order of magnitude lower on every measure.»
Eduardo Blázquez, «Practical Android Software Protection in the Wild» — An Appetizer,
blog de Quarkslab, 4 de junio de 2026. El artículo acompaña al trabajo revisado por pares
Blázquez, E. y Tapiador, J., «Practical Android Software Protection in the Wild»,
ACM Computing Surveys 58(2), art. 36, DOI 10.1145/3757735, pp. 36:1–36:32. El estudio
analizó 2.450.338 APK (el 99 % de su conjunto de datos), de los cuales «96.169
aplicaciones (~4 %) usaban al menos un packer, ofuscador o protector: 50.664 con packers,
45.320 con ofuscadores y 185 con protectores». La detección se hizo con APKiD.
Tres avisos que hay que arrastrar con la cifra:
- ⚠️ El 0,59 % se ha leído en el blog del primer autor, no en el artículo revisado:
dl.acm.org/doi/full/10.1145/3757735devuelve HTTP 403 y no hay preprint abierto. - ⚠️ El tamaño de la muestra concreta de Google Play detrás de ese 0,59 % no se ha podido confirmar: vive en tablas que el blog publica solo como imagen.
- El propio autor pone el límite, y hay que citarlo con la cifra: «como APKiD se apoya en reglas YARA para la detección, las técnicas anti-análisis no cubiertas por su conjunto de reglas pueden pasar desapercibidas, lo que potencialmente rebaja los resultados».
Y una trampa que hay que evitar activamente: AppSpear (RAID 2015) reporta un 0,58 % que se parece muchísimo y no es lo mismo —es sobre malware de 2013, otra población y otro año—. Confundirlos es fácil y el error se propaga.
6.2 Las otras mediciones
| Fuente | Población | N | Empaquetado |
|---|---|---|---|
| Blázquez y Tapiador, ACM CSUR 58(2) | Google Play | ⚠️ no confirmado | 0,59 % |
| Mauthe, Kargén, Shahmehri, SANER 2021 | Google Play | 13.601 | 127 apps (0,93 %, aritmética propia: el artículo solo publica conteos) |
| Ruggia et al., AsiaCCS 2024 | Google Play, más descargadas, abril de 2023 | 21.154 | 0 — «APKiD no pudo reconocer ningún packer ni protector» |
| Duan et al., NDSS 2018 | Malware, 2010–2015 | 93.910 | 13,89 % |
| Ruggia et al., AsiaCCS 2024 | Malware | — | 11,4 % |
| Mauthe, Kargén, Shahmehri, SANER 2021 | Malware, 2010–2016 | 24.553 | 131 muestras (0,53 %, aritmética propia) |
| Blázquez y Tapiador, ACM CSUR 58(2) | Huawei AppGallery / Qihoo 360 / Baidu | — | 43 % / 40 % / 25 % |
La conclusión que sostienen las siete filas a la vez, y que es lo que de verdad importa:
- En Google Play, el packer es una rareza estadística: por debajo del 1 % en las tres mediciones, y cero en la muestra de aplicaciones más descargadas.
- En malware, es un orden de magnitud más frecuente: del 11 % al 14 % en dos de las tres mediciones. La tercera, la de Mauthe et al., se queda en el 0,53 %, como Google Play.
- En los mercados chinos, es lo normal: del 25 % al 43 %.
El «40-43 % en los markets chinos» queda confirmado por la fila de AppGallery y Qihoo del mismo estudio.
6.3 El muestrario: cero packers
Buscadas las huellas de la «sección 5.1 · Por familia» en las 86 aplicaciones del muestrario —por nombre de
entrada del ZIP, en el base.apk y en cada split, y por contenido en los 323
ficheros classes*.dex— el resultado es ninguna coincidencia. No hay libjiagu, ni
libsecexe, ni libDexHelper, ni libshell*.so, ni assets/ijiami.dat, ni libcovault,
ni libv++, ni libdexprotector, ni libbaiduprotect, ni ninguna de las clases stub de la
tabla.
Es coherente con el 0,59 %: con 86 aplicaciones, la mayoría de F-Droid y el resto del catálogo internacional de Play, la expectativa estadística de encontrar un packer es inferior a una. El muestrario no está mal construido; es que el fenómeno no está ahí.
Consecuencia operativa: un detector de packers no se puede validar contra este muestrario. Cualquier prueba será de no-regresión y de ausencia de falsos positivos, que sigue siendo valiosa —un detector que dispara sobre 86 aplicaciones limpias es peor que ninguno— pero no mide el recall.
7. Qué se puede seguir haciendo con una app empaquetada
Encontrarse un packer no deja al analista a ciegas. Deja intacto todo lo que el packer no puede tocar porque es contrato con el sistema («Ofuscadores comerciales → §8 · Qué NO se puede ofuscar»).
Sigue disponible:
| Superficie | Qué se saca |
|---|---|
AndroidManifest.xml |
Nombre de paquete, versión, minSdk/targetSdk, permisos, componentes exportados, intent-filter, provider authorities, meta-data. El manifiesto no está cifrado, porque el sistema lo lee antes de ejecutar nada |
resources.arsc y res/ |
Cadenas de interfaz en todos los idiomas, colores, layouts. Con frecuencia el packer no toca los recursos |
| Firma | Certificado, cadena, esquemas v1–v4, lineage. Ver «Esquemas de firma» |
lib/*.so |
Análisis del binario nativo. Aquí está el descifrador y, en los packers de nivel 3, también la lógica de la aplicación |
Estructura del ZIP |
Orden de entradas, alineación, marcas de herramienta. Ver «Contenedor ZIP» |
| El propio stub | Nombres de clase, cadenas, y la biblioteca que carga: identifica al packer y con frecuencia también a su versión |
| Comportamiento observable | Tráfico de red, ficheros creados, permisos ejercidos. Análisis dinámico |
Ya no está disponible: el código Java de la aplicación, su grafo de llamadas, sus cadenas
literales, y todo análisis estático que dependa de ellos —detección de SDKs y trackers
por clase, SBOM desde el DEX, cobertura de decompilación—.
La regla honesta para un informe: decir que la aplicación está empaquetada, decir por qué familia si se sabe, y declarar explícitamente qué análisis quedan invalidados. Un informe que dice «0 trackers detectados» sobre una aplicación empaquetada está mintiendo por omisión, y esa es exactamente la clase de fallo silencioso que un producto serio no puede permitirse.
8. Recetas de identificación
Ejecutables con lo que hay instalado, sin herramientas específicas.
MUESTRAS=~/muestras-apk
BT=~/Library/Android/sdk/build-tools/37.0.0
APP=$MUESTRAS/com.termux_1002.apk
| Qué se busca | Orden |
|---|---|
Clase Application declarada |
$BT/aapt2 dump xmltree --file AndroidManifest.xml $APP | grep -A6 'E: application' |
| Bibliotecas nativas | unzip -l $APP 'lib/*' |
| Assets sospechosos | unzip -l $APP 'assets/*' | sort -k1 -n -r | head |
Número de clases del primer DEX |
$BT/dexdump -f <(unzip -p $APP classes.dex) | grep class_defs_size |
| Cadenas del stub | unzip -p $APP classes.dex | strings | grep -iE 'stubapp|shell|jiagu|secmain|dexhelper' |
| Entropía de un asset | unzip -p $APP assets/X | xxd | head — datos cifrados no tienen estructura visible |
Ejecutado sobre el Netflix del muestrario, el volcado del manifiesto da
android:name="o.KY", que no es una clase stub conocida sino el resultado del
repaquetado de DexGuard: la comprobación de la «sección 3.1 · El gancho: attachBaseContext» acierta al levantar la sospecha y
el resto del análisis la descarta.
La herramienta de referencia es APKiD:
apkid -j $MUESTRAS/com.termux_1002.apk
{
"apkid_version": "3.1.0",
"files": [
{
"filename": "…/com.termux_1002.apk",
"matches": { "manipulator": ["Resources Confusion"] }
},
{
"filename": "…/com.termux_1002.apk!classes.dex",
"matches": {
"anti_vm": ["Build.FINGERPRINT check", "Build.MANUFACTURER check",
"Build.BOARD check"],
"compiler": ["r8"]
}
}
],
"rules_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
Ejecutado con APKiD 3.1.0 el 13 de agosto de 2026. Un APK por fichero de entrada más una
entrada por cada classes*.dex, con la sintaxis fichero!entrada. El -j produce JSON
directamente utilizable; sin él, la salida es de texto con un |-> por etiqueta.
Y la lectura de este resultado concreto es más instructiva que la sintaxis. Termux es software libre, se compila desde fuente y no tiene protección alguna. APKiD le encuentra tres cosas y ninguna es lo que parece:
compiler: r8es correcto y útil: identifica con qué se compiló.anti_vmson tres comprobaciones deBuild.*que casi con seguridad vienen de una biblioteca de terceros —analítica o telemetría—, no de un intento de esquivar el análisis. La etiqueta describe una llamada presente, no una intención.manipulator: Resources Confusionaparece en 54 de los 58 APK standalone, incluidos los de F-Droid. Una técnica de manipulación deliberada que apareciese en nueve de cada diez aplicaciones limpias no sería una técnica: es una regla que dispara sobre algo que se ha vuelto normal. Lo desarrolla la «sección 7.1 de Ofuscadores comerciales · Lo que APKiD encuentra, y lo que no».
La lección práctica: las etiquetas de APKiD son evidencia, no veredicto. packer y
protector son las de alta precisión; anti_vm, anti_debug y manipulator hay que leerlas
con el contexto delante.
9. Por qué desempaquetar está fuera del alcance
No es mojigatería, y conviene tener el argumento escrito.
Es un pozo sin fondo por diseño. El repaso de la «sección 3.3 · Tres niveles de sofisticación» muestra la trayectoria: los
packers pasaron de liberar todo el DEX de una vez a liberarlo por capas —hasta 40 en
Tencent— y de ahí a no liberarlo nunca, traduciéndolo a un bytecode propio interpretado por
una máquina virtual generada por build. Cada avance del desempaquetado genérico se responde
con una capa más. Un desempaquetador es correcto contra la versión concreta del packer contra
la que se probó, y deja de serlo con la siguiente. El coste de mantenimiento no converge.
El público es minúsculo. Los datos de la «sección 6 · Prevalencia real» son inequívocos: por debajo del 1 % en Google Play, cero en la muestra de las más descargadas, y ni una en las 86 aplicaciones del muestrario. Construir la funcionalidad más cara del producto para el 0,59 % de los casos, y además la que peor envejece, es una mala asignación de esfuerzo aunque fuese gratis.
El riesgo legal es real y asimétrico. Extraer el payload de un protector comercial es una elusión de una medida técnica de protección, con tratamiento distinto en cada jurisdicción y, en varias, con excepciones para investigación en seguridad que no cubren la distribución de una herramienta de propósito general. Detectar e informar no tiene ese problema en ninguna parte: es leer un fichero y decir qué hay dentro.
Y hay una asimetría técnica a favor. La detección es barata —unos cuantos patrones sobre
el ZIP y sobre el DEX—, estable —las huellas estructurales de la «sección 5.2 · Huellas transversales, que valen para familias que aún no tienen regla» llevan años
sin cambiar—, verificable y publicable. Es, además, lo que el analista necesita el 100 % de
las veces: saber a qué se enfrenta. Desempaquetar solo lo necesita a veces, y quien lo
necesita de verdad tiene herramientas especializadas y un laboratorio.
La frontera resultante, en una frase: se identifica y se informa; no se extrae.
Fuentes
- APKiD — reglas YARA y datos del proyecto — https://github.com/rednaga/APKiD, ficheros
apkid/rules/apk/packers.yara,apk/protectors.yara,dex/packers.yara,elf/packers.yara,elf/protectors.yara,apkid/__init__.py,README.md,LICENSE.COMMERCIAL, leídos en crudo desderaw.githubusercontent.com/rednaga/APKiD/master/Consultados el 12 de agosto de 2026. De aquí sale íntegra la «sección 5 · Huellas de detección», y el recuento de reglas, la versión 3.1.0, la licencia dual y la autoría. - Blázquez, E. — «Practical Android Software Protection in the Wild» — An Appetizer —
https://blog.quarkslab.com/practical-android-software-protection-in-the-wild-an-appetizer.html
Consultado el 12 de agosto de 2026 (publicado el 4 de junio de 2026). De aquí sale el
0,59 % de la «sección 6.1 · El 0,59 %», el tamaño del conjunto de datos, el reparto entre packers,
ofuscadores y protectores, las cifras de los mercados chinos y el caveat sobre APKiD.
Artículo revisado subyacente: Blázquez, E. y Tapiador, J., ACM Computing Surveys 58(2),
art. 36, DOI 10.1145/3757735. ⚠️
dl.acm.org/doi/full/10.1145/3757735devolvió HTTP 403. - Duan, Zhang, Roy, Bianchi, Bao, Zhang, Wang, Xing — «Things You May Not Know About
Android (Un)Packers: A Systematic Study based on Whole-System Emulation», NDSS 2018 —
https://www.cs.ucr.edu/~heng/pubs/DroidUnpack_ndss18.pdf
Consultado el 12 de agosto de 2026. De aquí salen la definición de packer de la «sección 2 · Qué es un packer y por qué existe»,
la cita canónica sobre
attachBaseContextde la «sección 3.1 · El gancho: attachBaseContext», el número de capas por familia de la «sección 3.3 · Tres niveles de sofisticación» y el 13,89 % de malware de la «sección 6.2 · Las otras mediciones». ⚠️ La URLndss2018_01A-4_Duan_paper.pdfque circula está rota; la correcta en el sitio de NDSS esndss2018_04A-4_Duan_paper.pdf. - Zhang, Luo, Yin — «DexHunter: Toward Extracting Hidden Code from Packed Android
Applications», ESORICS 2015 — https://www4.comp.polyu.edu.hk/~csxluo/DexHunter.pdf
Consultado el 12 de agosto de 2026. De aquí salen la lista de clases
Applicationstub por familia y la nota sobreApplicationWrapper/SuperApplicationde la «sección 3.1 · El gancho: attachBaseContext», y los nombres de asset de la «sección 5.1 · Por familia». - Yang, Zhang, Wang, Zhu, Xue — «AppSpear: Bytecode Decrypting and DEX Reassembling for
Packed Android Malware», RAID 2015 —
https://link.springer.com/chapter/10.1007/978-3-319-26362-5_17 (DOI 10.1007/978-3-319-26362-5_17)
Consultado el 12 de agosto de 2026. De aquí salen full-code releasing, incremental code
releasing y el cifrado en dos capas de Baidu («sección 3.3 · Tres niveles de sofisticación»), y el 0,58 % que la «sección
6.1 · El 0,59 %» advierte de no confundir.
El PDF que circula en
gosec.sjtu.edu.cn/publications/RAID2015Yang.pdfya no existe (404 el 12 de agosto de 2026); se sustituye por la entrada editorial, que es estable. - Xue, Luo et al. — «PackerGrind», «Happer», «Parema» —
https://www4.comp.polyu.edu.hk/~csxluo/PackerGrind.pdf,
https://www4.comp.polyu.edu.hk/~csxluo/Happer.pdf,
https://www4.comp.polyu.edu.hk/~csxluo/Parema.pdf
Consultados el 12 de agosto de 2026. De aquí salen el gancho de
ClassLinker::LoadMethody losArtMethodde la «sección 3.2 · Las tres vías de carga», la JNI Transformation y la P-VM de la «sección 3.3 · Tres niveles de sofisticación». - Thomas, R. — «A Glimpse into Tencent's Legu Packer» — https://blog.quarkslab.com/a-glimpse-into-tencents-legu-packer.html Consultado el 12 de agosto de 2026 (publicado el 26 de noviembre de 2019). De aquí sale la corroboración independiente de la «sección 5.3 · Corroboración independiente».
DexClassLoader,InMemoryDexClassLoader,PathClassLoader,BaseDexClassLoader— https://developer.android.com/reference/dalvik/system/DexClassLoader, https://developer.android.com/reference/dalvik/system/InMemoryDexClassLoader Consultados el 12 de agosto de 2026. De aquí sale la tabla de vías de carga de la «sección 3.2 · Las tres vías de carga», los niveles de API de cada constructor y la corrección sobre la no deprecación deDexClassLoader.ApplicationyContextWrapper.attachBaseContext— https://developer.android.com/reference/android/app/Application, https://developer.android.com/reference/android/content/ContextWrapper Consultados el 12 de agosto de 2026. De aquí salen las dos citas de la «sección 3.1 · El gancho: attachBaseContext» sobreandroid:namey sobre el momento de instanciación.- Sitios de los fabricantes —
shell.virbox.com,dexprotector.com,licelus.com/products/dexprotector,promon.io/about-us,ijiami.cn,apkprotect.baidu.com,jiagu.360.cn,docs.doverunner.com,guardsquare.com/dexguardConsultados el 12 de agosto de 2026. De aquí sale la tabla de la «sección 4 · Las familias», incluido el estado de cada sitio. Fallaron:bangcle.com(HTTP 500),secneo.com(NXDOMAIN), los portales de producto de Tencent (404 y reto anti-bot),senseshield.com(NXDOMAIN),jaq.alibaba.com(NXDOMAIN) yaliyun.com/product/security/mobilesecurity(404). - Mauthe, Kargén, Shahmehri, SANER 2021 — https://www.ida.liu.se/~ulfka17/papers/SANER2021.pdf y Ruggia et al., AsiaCCS 2024 — https://s3.eurecom.fr/docs/asiaccs24_ruggia.pdf Consultados el 12 de agosto de 2026. De aquí salen las filas correspondientes de la tabla de la «sección 6.2 · Las otras mediciones». La de malware de Mauthe et al. (131 de 24.553 muestras, tabla «Packed (APKiD)») se añadió tras consultarlo de nuevo el 25 de septiembre de 2026.
- APKiD 3.1.0 ejecutado localmente — instalado con
pipel 13 de agosto de 2026 y ejecutado sobrecom.termux_1002.apkdel muestrario. De aquí sale la salida JSON de la «sección 8 · Recetas de identificación» y la lectura de sus tres etiquetas. rednaga/APKiD, comparación dev3.1.0conmaster— https://raw.githubusercontent.com/rednaga/APKiD/v3.1.0/apkid/rules/ y https://raw.githubusercontent.com/rednaga/APKiD/master/apkid/rules/, más https://api.github.com/repos/rednaga/APKiD/releases/latest Consultado el 25 de septiembre de 2026. De aquí sale quedex/packers.yara,elf/packers.yarayapk/protectors.yarason idénticos en las dos, queapk/packers.yarasolo difiere en el escape de un punto enlibalice.so, quemasterañadedexprotector_benelf/obfuscators.yaray quev3.1.0, del 9 de abril de 2026, sigue siendo la última versión publicada («sección 5 · Huellas de detección»).- Zheng, Hou, Chen, Ren, Li, Li, Shen — «Gupacker: Generalized Unpacking Framework for
Android Malware» — IEEE Transactions on Information Forensics and Security, vol. 20,
2025, pp. 4338-4352. DOI
10.1109/TIFS.2025.3558592, https://doi.org/10.1109/TIFS.2025.3558592 Consultado el 25 de septiembre de 2026: metadatos de Crossref y resumen de la API de Semantic Scholar. De aquí salen las tres generaciones de packers de la «sección 3.3 · Tres niveles de sofisticación». - DexGuard 8.1 released (Guardsquare) — https://www.guardsquare.com/blog/dexguard-81-released Consultado el 25 de septiembre de 2026. Entrada del 30 de noviembre de 2017. De aquí sale el «Code packing» de la «sección 4 · Las familias».