1. Resumen Ejecutivo

Se realizó un análisis exhaustivo de tráfico de red sobre un dispositivo Android TV Box que ejecuta una aplicación IPTV no autorizada. El trabajo comprendió la captura y análisis de ocho sesiones de tráfico de red, la decompilación del APK, y el diseño de controles de red en un router Mikrotik para forzar el tráfico de autenticación de la aplicación a través de una VPN específica.

La aplicación utiliza técnicas avanzadas de evasión: dominios generados algorítmicamente (DGA), múltiples IPs hardcodeadas en el código nativo encriptado, y una lista exhaustiva de resolvers DNS alternativos (DoH, DoT, DNS público chino). La solución final implementada fue una ruta estática por rangos de IP de Cloudflare a través del túnel WireGuard, que resultó ser el enfoque más robusto al operar a nivel de routing, independientemente de cualquier mecanismo de resolución DNS que use la aplicación.

ParámetroValor
DispositivoAndroid TV Box — Android 11
IP local172.16.2.253 (LAN 172.16.2.0/24)
APK principalcom.android.mgstv / com.android.msandroid
Versión APK43404 → 43405 (auto-actualización detectada)
Serial device (SN)5936037ae199ee3fec36ef6c65446dc6
User ID asignado783031970 (cuando autenticado)
RouterMikrotik con WireGuard (wireguard1) hacia México
Capturas analizadas8 archivos PCAP — 3.003 a 18.518 paquetes c/u
Solución finalRutas estáticas por rango Cloudflare vía WireGuard

2. Infraestructura de la Aplicación

2.1 Endpoints HTTP identificados (puerto 80 — texto plano)

Los siguientes endpoints fueron capturados en texto claro, permitiendo análisis completo de requests y parámetros:

HostIPEndpoint / Función
emowvv.dqiswip4.xyz172.67.212.138POST /api/portalCore/checkForceBind — Auth principal
vgwbm.uwfyobivh.com104.21.2.120GET /epg/v2/live/app/utc-3/26 — Guía de canales EPG
yvhcn.hxjebagrv.com172.67.129.19POST /api/adserver/v2/get_content — Publicidad
nxiqj.jgrqyxupl.com172.67.148.10GET /notice/api/get_notice — Notificaciones
zxiws.tcgwhnvym.com104.21.29.101GET /notice/api/get_notice — Notificaciones (backup)
iyut.xgw3sdzoac.com172.67.168.132GET /MarketServer/update — Actualizaciones APK
sgyc.bfj1k2g4v.com104.18.53.7GET /v1/stargazer — WebSocket persistente
64.31.8.44:2345564.31.8.44GET /live/*.m3u8, *.ts — Streaming IPTV (HLS)

2.2 Endpoints HTTPS identificados (por SNI/TLS)

Host (SNI)IPFunción identificada
emowvv.dqiswip4.xyz172.67.212.138 / 104.21.85.241Portal core — Auth HTTPS (failover)
eskna.ucpjdhivl.com172.67.141.221Desconocido — dominio DGA
giernh.ss58oknn.com104.21.3.74Desconocido — dominio DGA
sfgknh.qho3cnsyil.com104.18.52.105Desconocido — dominio DGA
oslogs.umeng.com47.246.109.109Analytics Umeng (Alibaba) — logs
utoken.umeng.com223.109.148.139Autenticación Umeng
audid.umeng.com123.183.232.13Device ID fingerprinting Umeng
play.googleapis.com216.239.32.223Google Play Services
mtalk.google.com142.250.0.188Firebase Cloud Messaging (FCM)
hwlauncher-apps-o.api.leiniao.com8.209.78.228API launcher Leiniao/Huawei

2.3 Streaming IPTV

El servidor de streaming principal fue 64.31.8.44:23455. El dispositivo descarga segmentos MPEG-TS (.ts) cada aproximadamente 5 segundos mediante HLS, con dos streams simultáneos activos durante las sesiones normales. Se identificaron dos streams activos:

  • Stream 1: /live/cyx_93531158996778016/*.ts — descargando segmentos secuenciales cada 5 seg.
  • Stream 2: /live/cyx-E82F71FB4B37af64B2F9D947B8FC/*.ts — segundo canal en buffer.

El tráfico de streaming representó más de 13.800 paquetes (~1 MB) en una sola sesión de captura, siendo el flujo más voluminoso de toda la captura.

2.4 Tráfico P2P cifrado

Se identificó tráfico UDP cifrado hacia aproximadamente 50 IPs externas en puertos altos aleatorios. Los payloads comienzan con el byte 0x40 y tienen tamaños fijos de 244 y 260 bytes, sugiriendo un protocolo P2P propietario del sistema IPTV. Varios IPs distintos comparten el mismo puerto de destino (37111), patrón típico de swarm P2P.

  • 190.89.30.70:44845 — 313 paquetes UDP (P2P activo en cap. 8)
  • 38.165.228.24:30910 — 188 paquetes UDP (P2P activo en cap. 8)
  • 181.94.228.29:63455 — 288 paquetes (mayor flujo P2P en cap. 3)

3. Análisis del APK

3.1 Protección — Packer IJM (ijiami)

La aplicación está protegida con el packer comercial IJM (ijiami), ampliamente utilizado en aplicaciones Android del mercado chino. Esta protección hace que el código Java/Kotlin real nunca esté disponible en texto plano en el disco — se desencripta únicamente en memoria en tiempo de ejecución.

ComponenteDescripción
s.h.e.l.l.SApplication wrapper — carga libexec.so y desencripta el DEX real
s.h.e.l.l.NInterfaz JNI con funciones nativas — b2b(), al(), l(), r(), ra()
s.h.e.l.l.AAppComponentFactory — intercepta instanciación de componentes Android
libexec.soLoader nativo — desencripta ijiami.ajm en memoria (5.279 strings)
libexecmain.soDispatcher nativo — tabla de funciones x.101 a x.255 (457 strings)
ijiami.ajmDEX real encriptado — 2,3 MB — formato indl01 — AES-128-CBC
ijiami.datDatos encriptados — 4,5 MB — firma: 8030d1966a1056d907f85a2d43894f84

3.2 Esquema de configuración de dominios

Se encontró el archivo assets/domain_test.json con el esquema de configuración de endpoints. Los valores ‘xx’ son placeholders — los valores reales se inyectan desde el DEX desencriptado en runtime:

{

  «portal_main»:       «xx»,   // emowvv.dqiswip4.xyz

  «portal_backup»:     «xx»,

  «epg_main»:          «xx»,   // vgwbm.uwfyobivh.com

  «epg_backup»:        «xx»,

  «market_main»:       «xx»,   // iyut.xgw3sdzoac.com

  «notice_main»:       «xx»,   // nxiqj / zxiws.tcgwhnvym.com

  «ad_main»:           «xx»,   // yvhcn.hxjebagrv.com

  «dccore_main»:       «xx»,   // emowvv.dqiswip4.xyz

  «diamond_main»:      «xx»,

  «datacollect_main»:  «xx»

}

3.3 Assets relevantes identificados

ArchivoTamañoDescripción
assets/ijiami.ajm2,3 MBDEX real encriptado — formato indl01 — contiene dominios e IPs
assets/ijiami.dat4,5 MBDatos encriptados adicionales
assets/domain_test.json542 BEsquema de configuración de endpoints (valores en runtime)
assets/signed.bin71 KBVerificación de firma del APK — bloquea reempaquetado
assets/IJMDal.Data17 KBDatos del packer IJM
assets/images/*_encrypted.png16-32 BBlobs de datos cifrados disfrazados de imágenes PNG
lib/arm64-v8a/libexec.soLoader nativo principal
lib/arm64-v8a/libumeng-spy.soSDK de telemetría Umeng/Alibaba

3.4 Observaciones de seguridad del APK

  • Los dominios de API tienen nombres generados algorítmicamente (DGA-like) — evitan listas negras estáticas.
  • Todos los dominios resuelven a IPs de Cloudflare, lo que dificulta el bloqueo por IP individual.
  • El body del POST /api/portalCore/checkForceBind viaja con datos en Base64 + cifrado simétrico — envía el serial del dispositivo.
  • SDK Umeng (Alibaba) recopila telemetría detallada y la envía a servidores en China (47.246.109.109, 223.109.x.x).
  • El APK verifica su propia firma antes de desencriptar — no puede reempaquetarse sin romper el mecanismo.
  • La app incluye código de anti-debugging que inspecciona /proc/self/maps buscando libaoc.so, libart.so y linker64.

4. Análisis de las Capturas — Cronología

4.1 Captura 3 — Sesión normal autenticada (línea base)

INFO18.518 paquetes — Sesión completamente funcional con streaming activo

Primera captura de referencia. El dispositivo inicia desde una IP sin restricciones y completa el flujo de autenticación exitosamente.

  • checkForceBind ejecutado con éxito → userId=783031970 asignado
  • EPG descargado (~12 MB) desde vgwbm.uwfyobivh.com
  • Streaming IPTV activo: 13.816 paquetes TCP a 64.31.8.44:23455 (~1 MB)
  • WebSocket /v1/stargazer activo en sgyc.bfj1k2g4v.com
  • Tráfico P2P UDP a ~50 IPs externas en puertos altos
  • SDK Umeng activo: oslogs, utoken, audid hacia servidores Alibaba en China

4.2 Captura 4 — IP bloqueada, acceso restringido al home

BLOCK3.003 paquetes — checkForceBind falla — userId vacío — sin streaming
  • checkForceBind ejecutado (pkt #85-#479) pero respuesta del servidor indica IP bloqueada
  • userId= vacío en todos los requests subsiguientes
  • EPG descargado igualmente (~12 MB) — el home se muestra pero sin contenido autenticado
  • Sin streaming IPTV, sin WebSocket, sin publicidad
  • La captura es asimétrica (solo tráfico saliente visible)

4.3 Capturas 5, 6, 7, 8 — Proceso de bloqueo progresivo

Cap.Acción tomadaComportamiento appResultado
5Bloqueo DoH 8.8.8.8:443Usa IP cacheada 104.21.85.241 hardcodeada. DNS UDP:53 sigue pasando.IP real en uso — sin efecto
6Bloqueo extendido DoHDetecta DoT (puerto 853) hacia 172.16.2.1. Conecta a emowvv antes del DNS.IP hardcodeada ignorada en DNS
7Bloqueo IP 172.67.212.138Failover automático a IP backup 104.21.85.241. userId= vacío.Failover a IP backup
8Bloqueo rango CF completoEscala a DoH en 1.1.1.1:443 (conecta), 223.5.5.5:443 (AliDNS), 9.9.9.10:443 (Quad9).Nuevos resolvers DoH activos

5. Mecanismos de Evasión Identificados

5.1 Resolución DNS — Cascada de fallbacks

La aplicación implementa una estrategia de resolución DNS en cascada con múltiples fallbacks, todos hardcodeados en el DEX encriptado:

OrdenServidorIPPuerto/ProtoEstado en cap. 8
1Google DoH8.8.8.8443 TCPBloqueado ✗
2Cloudflare DoH1.1.1.1443 TCPConecta — activo ✓
3AliDNS DoH223.5.5.5443 TCPIntentando
4Quad9 DoH9.9.9.10443 TCPIntentando
5Google DNS8.8.8.853 UDPPasa (regla dstnat ineficaz)
6Local resolver172.16.2.153 UDP / 853 DoTPasa — responde IP real
7IP hardcodeadaVarias CFBypasea DNS completamente

5.2 IPs hardcodeadas en el DEX encriptado

Este es el mecanismo más crítico: la aplicación tiene múltiples IPs hardcodeadas dentro del ijiami.ajm y las usa directamente sin necesidad de consultar DNS. La evidencia es clara en múltiples capturas donde la conexión se establece antes de que se realice cualquier query DNS:

  • Captura 5/6: Conecta a 104.21.85.241:443 en pkt[37], DNS query en pkt[86] — 49 paquetes después
  • Captura 7: Conecta a 104.21.85.241:443 en pkt[38], DNS query en pkt[99] — 61 paquetes después
  • Captura 8: Con rango CF bloqueado, escala a nuevos resolvers DoH para obtener nuevas IPs

IPs de Cloudflare confirmadas para emowvv.dqiswip4.xyz a lo largo de las capturas:

172.67.212.138   — usada en cap. 4, 5, 6

104.21.85.241    — usada en cap. 5, 6, 7 (failover automático)

5.3 Verificación de firma anti-reempaquetado

El loader libexec.so verifica la firma del APK (MD5: 8030d1966a1056d907f85a2d43894f84) antes de desencriptar el DEX real. Cualquier modificación del APK — incluyendo intentar parchear los dominios hardcodeados — rompe la verificación y el loader no desencripta. Esto hace inviable el parcheo estático del APK.

5.4 Anti-debugging

El código en s.h.e.l.l.S.il() lee /proc/self/maps y detecta la presencia de entornos de análisis buscando las librerías:

/lib64/libart.so

/lib64/libaoc.so

/bin/linker64

Si detecta un entorno instrumentado, puede alterar su comportamiento o negarse a desencriptar.

6. Controles de Red Implementados en Mikrotik

6.1 Solución final — Rutas estáticas por rango Cloudflare

Luego de iterar a través de múltiples enfoques (DNS estático, DNAT, bloqueo de IPs individuales), la solución más robusta fue implementar rutas estáticas que fuerzan todo el tráfico hacia los rangos de IP de Cloudflare a salir por el túnel WireGuard hacia México. Esta solución opera a nivel de routing, lo que la hace independiente de cualquier mecanismo DNS que use la aplicación.

/ip route

add dst-address=104.16.0.0/12 gateway=wireguard1 comment=»Cloudflare via MX»

add dst-address=172.64.0.0/13 gateway=wireguard1 comment=»Cloudflare via MX»

6.2 Evolución de los controles implementados

ControlImplementaciónResultado
DNS estático/ip dns static add name=emowvv… address=172.31.255.1Ineficaz — app usa IPs hardcodeadas y DoH
Intercepción DNS UDP:53dstnat redirect to-ports=53 in-interface=bridgeParcialmente ineficaz — regla no aplicaba correctamente
Bloqueo DoH 8.8.8.8:443filter drop dst-address=8.8.8.8 dst-port=443Efectivo para 8.8.8.8 — app escala a 1.1.1.1
Bloqueo IP individualfilter drop dst-address=172.67.212.138App hace failover a 104.21.85.241 automáticamente
Bloqueo rango CFfilter drop dst-address-list=cloudflare_rangesApp escala a nuevos DoH — EPG seguía pasando
Ruta estática CF→WG/ip route add 104.16.0.0/12 gateway=wireguard1SOLUCIÓN FINAL — independiente de DNS

6.3 Controles adicionales recomendados

Para un bloqueo completo de todos los canales DNS alternativos identificados:

# Bloquear todos los DoH conocidos usados por la app

/ip firewall address-list

add list=doh_servers address=8.8.8.8     comment=»Google DoH»

add list=doh_servers address=1.1.1.1     comment=»Cloudflare DoH»

add list=doh_servers address=1.0.0.1     comment=»Cloudflare DoH alt»

add list=doh_servers address=223.5.5.5   comment=»AliDNS»

add list=doh_servers address=223.6.6.6   comment=»AliDNS alt»

add list=doh_servers address=9.9.9.9     comment=»Quad9″

add list=doh_servers address=9.9.9.10    comment=»Quad9 ECS»

/ip firewall filter

add chain=forward action=drop protocol=tcp dst-port=443 \

    dst-address-list=doh_servers src-address=172.16.2.253

# Interceptar todo DNS UDP/TCP desde el dispositivo

/ip firewall nat

add chain=dstnat action=redirect to-ports=53 protocol=udp \

    dst-port=53 src-address=172.16.2.253 place-before=0

# Bloquear DoT puerto 853

add chain=forward action=drop protocol=tcp dst-port=853 \

    src-address=172.16.2.253

7. Fingerprint del Dispositivo

Los siguientes parámetros identifican de manera única al dispositivo en todas las capturas:

ParámetroValor
IP local172.16.2.253
Serial (SN)5936037ae199ee3fec36ef6c65446dc6
User ID783031970 (cuando autenticado)
APK packagecom.android.mgstv / com.android.msandroid
APK versión43404 → 43405
Build fecha APK2024-11-11 14:53:20_30_11_
OSAndroid 11
User-Agent HTTPokhttp/3.12.12
Firma APK (MD5)8030d1966a1056d907f85a2d43894f84
Timezone EPGUTC-3 (Argentina)
Idiomaes (español)
mDNS device nameTV-Oficina

8. Trabajo Pendiente — Dump con Frida

Para obtener la lista completa de IPs hardcodeadas y la configuración real de dominios dentro del DEX encriptado, se requiere un dump de memoria en runtime usando Frida. El setup requerido ya fue identificado:

8.1 Setup disponible

  • Android Studio con AVD configurado y app instalada
  • Emulador con acceso root (imagen AOSP sin Google Play)
  • frida-tools instalado en PC
  • frida-server para x86_64 listo para subir al emulador

8.2 Comando para dump

# Subir frida-server al emulador

adb push frida-server /data/local/tmp/

adb shell chmod 755 /data/local/tmp/frida-server

adb shell ‘su -c «/data/local/tmp/frida-server &»‘

# Dump de todos los DEX en memoria (incluye el desencriptado)

frida-dexdump -U -f com.android.mgstv –deep -o ./dex_dump/

# Buscar dominios e IPs en el DEX dumpeado

strings dex_dump/*.dex | grep -E ‘dqiswip4|portalCore|portal_main|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]’

8.3 Resultado esperado

El dump debería revelar la lista completa de IPs hardcodeadas (actualmente se conocen al menos dos: 172.67.212.138 y 104.21.85.241), la lista de resolvers DoH en orden de prioridad, y los valores reales del archivo domain_test.json que actualmente muestran ‘xx’ como placeholder.

9. Estado Final de Controles

ControlDescripciónEstado
DoH Google (8.8.8.8:443)Bloqueo TCP port 443 dst 8.8.8.8OK
DoH Cloudflare (1.1.1.1:443)Pendiente en bloqueo DoH listPEND
DoH AliDNS (223.5.5.5:443)Pendiente en bloqueo DoH listPEND
DoH Quad9 (9.9.9.10:443)Pendiente en bloqueo DoH listPEND
DoT puerto 853Bloqueo TCP/UDP port 853PEND
DNS UDP:53 intercepcióndstnat redirect — regla verificarPEND
IP 172.67.212.138 bloqueadafilter drop chain forwardOK
IP 104.21.85.241 bloqueadaIncluida en rango CFOK
Rango 104.16.0.0/12 via WGRuta estática wireguard1OK
Rango 172.64.0.0/13 via WGRuta estática wireguard1OK
Auth emowvv bloqueadauserId= vacío en todos los requestsOK
Streaming IPTVSin .ts / .m3u8 en capturas post-bloqueoOK

10. Conclusiones

La aplicación IPTV analizada implementa un conjunto sofisticado de mecanismos de evasión que van más allá de lo típico en apps de esta categoría: packer comercial con verificación de firma, IPs de destino hardcodeadas en código nativo encriptado, y una cascada de resolvers DNS alternativos (DoH/DoT hacia Google, Cloudflare, AliDNS y Quad9) para garantizar conectividad incluso ante bloqueos parciales.

La solución de rutas estáticas a nivel de routing resultó ser la más efectiva porque opera por debajo de todos los mecanismos de evasión de la app: no importa qué DNS use, no importa qué IP obtenga para el dominio de auth — si esa IP cae en el rango de Cloudflare, el paquete sale por el túnel WireGuard hacia México.

El análisis completo de las IPs hardcodeadas en el DEX queda como tarea pendiente con el dump de Frida, lo que permitiría refinar los controles si en el futuro la app agrega IPs fuera del rango de Cloudflare.

Por Gustavo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *