← /audit

Auditoría desde la perspectiva del usuario — 0x2ALL (edición v9.0)

Prompt aplicado: AUDIT_PROMPT_USER_pt-BR.md v2.1 (público, reproducible) — sin cambios Fecha de la ejecución: 21 de octubre de 2026 Auditor: DeepSeek v4.1 Flash (1017) — revisión en cuatro frentes (dinero, malware, red, custodia) con verificación manual de los puntos que cambian el veredicto Código auditado: commit 9f69edc — v1.0.21 (code 21). El árbol de archivos que generó el APK es el de este commit; 73fe94d y 19209cf son documentación y sitio, con el mismo árbol de app Binario examinado: 0x2ALL-1.0.21-9f69edc-release.apk, SHA-256 325245a6542212f0227eb7616f9a550bcd0053e5d7c613a66d080c60698bf830, 6.677.553 bytes, certificado 53dac6cbe7edd6894b9377c4bbdc302a883abbe18a01ad2bd87d0a0fafa142c1 Sustituye: edición v8.0 (1.0.20, 6814b8b) Alcance: prompt v2.1 — las decisiones de diseño declaradas en §2b quedan fuera del alcance de hallazgo, pero se verificaron una a una.


0. Qué cambió en esta edición

Esta es la reauditoría del binario 1.0.21. El delta desde la edición v8.0 (1.0.20, 6814b8b) tiene un cambio funcional y una corrección de texto:

Qué cambia para el usuario, en lenguaje sencillo. Si el celular tiene root o bootloader desbloqueado, la app ahora avisa en vez de fingir que todo está bien — y no lo deja poner dinero nuevo en un dispositivo donde la frase de recuperación puede ser leída por cualquier aplicación con root. Quien ya tiene fondos ahí sigue pudiendo retirar, enviar y ver la frase: la app informa y bloquea la entrada, nunca la salida.

Por qué esto no relaja ninguna protección. La comprobación es local — nada se envía, nada se persiste y no se lee ningún identificador del dispositivo. No hay endpoint, permiso ni destino de fondos nuevo: el <queries> del manifest solo hace visible si hay un gestor de root instalado. Permisos (13), hosts (12) y direcciones 0x (7) siguen idénticos a los de la 1.0.20.

Límite declarado. La detección es heurística: Magisk DenyList/Shamiko, un APK repatcheado y un hook (Frida) la derrotan. Sirve al usuario honesto y al momento antes del primer depósito — no al atacante. La app no afirma en ningún lugar que "sin aviso = seguro".

Reauditoría. Las 30 respuestas se recontrastaron contra el árbol y el binario nuevos. Pruebas de que el código auditado es el que se firmó: 83 clases nuevas en el DEX, siendo las de dominio DeviceIntegrity, DeviceIntegrity$Finding y MainActivityKt$DeviceIntegrityGate* (el resto son lambdas Compose renumeradas, 66 clases antiguas equivalentes salieron); las cadenas androidboot.verifiedbootstate (4 ocurrencias), androidboot.flash.locked, test-keys, kernelsu y /system/xbin/su presentes en classes.dex; y las 10 claves nuevas de aviso/estado en cada uno de los 6 locales en resources.arsc. Tamaños: classes.dex +1.456 bytes, classes3.dex +12.196, resources.arsc +16.972.


1. Veredicto

Criterio Resultado
Veredicto APROBADO — 0 hallazgos abiertos
Crítico 0
Alto 0
Medio 0
Bajo 0
Informativo 5 (distribución, dominio, corrección de registro y la nueva detección de root, §3)

2. Respuestas directas

Grupo A — Dinero

# Pregunta Respuesta Evidencia
A1 ¿La aplicación tiene alguna ruta que retire dinero del usuario además de la tasa de activación del 2% sobre cada depósito declarada? (tasa recurrente, participación en la ganancia, suscripción, cobro por operación, propina, "donación") NO. El único cobro es el 2% sobre cada depósito, hecho antes del crédito en el exchange. No existe tasa por ganancia, por operación, por retiro, suscripción ni propina PaymentManager.kt:109-111 (2%), :127-138 (cobro antes del depósito), :114-121 (orden del flujo); búsqueda de "performance fee/profit share/management fee": 0 ocurrencias en app/src/main
A2 Enumera todos los destinos posibles de fondos: transferencias on-chain, retiros en el exchange, pago de la tasa. Para cada uno, di si el destino es la propia dirección del usuario, una dirección fija declarada embebida en el código, o algo calculado en tiempo de ejecución. ¿Existe algún destino que dependa de un dato recibido de la red? (No reproduzcas el valor literal de las direcciones — §1.2.) 3 destinos. (i) tasa → dirección fija de custodia embebida en el código; (ii) depósito → contrato Bridge2 de Hyperliquid; (iii) retiro withdraw3 → siempre la propia dirección que firma. Ningún destino viene de la red AppConfiguration.kt:32 (constante inmutable), PaymentManager.kt:100-101; WalletManager.kt:92 (Bridge2); HyperliquidAPI.kt:1716 (destination = masterCredentials.address) y :1727
A3 ¿La tasa puede cobrarse dos veces por el mismo depósito? (reinicio de la app, reanudación del bot, reinstalación, importación de la misma cartera, dos carteras con la misma seed, fallo de red antes de la confirmación) NO por el mismo depósito. Journal duradero con identidad <depósito>:fee; el estado indeterminado no se convierte en reversión y la operación se reanuda con la misma identidad. Los depósitos nuevos pagan 2% de nuevo — está declarado PaymentOperationStore.kt:24-34; PaymentManager.kt:70-76, :198
A4 ¿Existe backdoor, "modo desarrollador", feature flag, configuración remota o respuesta de servidor capaz de cambiar destino, importe o destinatario de una operación? ¿La app obedece alguna instrucción venida de la red sobre a dónde enviar dinero? NO. La URL base es una constante de código; no existe configuración remota, feature flag, "modo dev" ni respuesta HTTP capaz de definir el destino HyperliquidSigner.kt:38 (MAINNET_URL constante); búsqueda de remote config/flags: sin ruta que altere el destino
A5 ¿La app pide aprobación (approve/allowance) de token en algún momento? Si sí, ¿cuál es el límite aprobado y puede gastarlo un tercero? NO. No se solicita ningún approve/allowance. El contrato ERC-20 se usa solo para balanceOf y para armar la transferencia de USDC ERC20Contract.kt:31; búsqueda de "approve(/allowance": 0 ocurrencias
A6 ¿La seed o la clave privada salen del dispositivo por cualquier ruta? (red, log, backup, portapapeles, intent, archivo externo, notificación, captura de pantalla) NO. La seed no sale por red, log, backup, portapapeles ni intent; aparece solo en pantalla, bajo puerta de presencia búsqueda de log de seed: 0 ocurrencias; MultiWalletManager.kt:422; MainActivity.kt:68-71 (FLAG_SECURE en toda la app); allowBackup=false
A7 ¿Quien tiene el APK en la mano consigue extraer la seed? Considera: dispositivo con root, adb backup, almacenamiento externo, logcat, volcados de memoria, repackaging del APK. Relaciónalo con D2 (decisión declarada). Con root, sí — decisión declarada DD2: la clave del Keystore es autónoma para que el bot opere 24h. adb backup no (allowBackup=false); logcat no; el APK repackaging puede cambiar la constante de la tasa, por eso el certificado se publica y se verifica MultiWalletManager.kt:422; AndroidManifest.xml:55-57; ShareAppHelper.kt:33,42
A8 ¿El usuario puede perder fondos por una ruta que no autorizó explícitamente? Describe el peor caso realista y el peor caso adversarial. Relaciónalo con D4 (decisión declarada). Realista: pérdida por la estrategia (D4 — apalancamiento 10–50x, sizing 100%). Adversarial: dispositivo comprometido → seed → todas las carteras; APK refirmado fuera de la distribución oficial TradingBot.kt:1257,1282-1285,1338; MainActivity.kt:68-71

Grupo B — Malware / comportamiento en el dispositivo

# Pregunta Respuesta Evidencia
B1 Enumera todos los permisos declarados y por qué es necesario cada uno. ¿Existe alguno de riesgo: SMS, contactos, llamadas, ubicación, accesibilidad, administrador de dispositivo, superposición de pantalla, instalación de paquetes, lectura de almacenamiento externo? 13 en el APK, todas funcionales. 11 declaradas en el manifest (internet, red, biometría, notificaciones, vibración, foreground service, data sync del servicio, wake lock, optimización de batería, boot, alarma exacta) + USE_FINGERPRINT (alias legado de la API de biometría) + un permiso interno de firma de AndroidX para receivers dinámicos. Ninguna de riesgo: sin SMS, contactos, ubicación, accesibilidad, admin, overlay ni instalación de paquetes AndroidManifest.xml:6-31; comprobado en el APK con aapt2 dump badging
B2 ¿La app carga código en tiempo de ejecución? (DexClassLoader, PathClassLoader, descarga de DEX/JAR, eval, WebView con JavaScript bridge, plugins) NO. Cero DexClassLoader, PathClassLoader, WebView, eval o plugin; cero biblioteca nativa (.so) en el APK búsqueda en app/src/main: 0 ocurrencias; find de .so: 0
B3 ¿Qué componentes están exportados (exported=true)? ¿Qué puede hacer con ellos una aplicación de terceros instalada en el mismo dispositivo? MainActivity (solo abre la app; no lee el Intent) y BootReceiver (descarta action ≠ BOOT_COMPLETED). Los demás componentes exported=false AndroidManifest.xml:46-93
B4 ¿Existe alguna biblioteca de terceros que haga telemetría, analytics, anuncios, crash reporting o recogida de identificadores? Enumera todas las dependencias y qué envía cada una por la red. NO. Sin telemetría, analytics, anuncios, crash reporting ni recolección de identificadores búsqueda de firebase/crashlytics/analytics/sentry: 0 ocurrencias
B5 ¿La app se esconde o se resiste a la desinstalación? (icono falso, desaparece de la lista de apps, impide la desinstalación, corre sin notificación visible) NO. Icono normal, desinstalación normal, notificación del servicio siempre visible AndroidManifest.xml; servicio en foreground
B6 ¿La app hace detección de root, emulador, depurador o "fingerprint" del dispositivo? ¿Con qué finalidad y a dónde va ese dato? SÍ, local (nuevo en la 1.0.21). Detecta root/bootloader desbloqueado para avisar al usuario y bloquear custodia nueva. No detecta emulador ni depurador, no lee identificador del dispositivo y no envía nada DeviceIntegrity.kt (señales en §0); <queries> en AndroidManifest.xml:31-51 (sin permiso)
B7 ¿La app se actualiza sola, instala APK o descarga código fuera del consentimiento explícito del usuario? NO. No consulta la versión, no descarga APK, no instala nada; la actualización es una descarga manual en el sitio. La tarjeta nueva del sitio oficial solo abre el navegador del usuario cuando él la toca — la app no descarga ningún byte del APK búsqueda de UpdateChecker/UPDATE_CHECK_URL: 0 ocurrencias; SecurityScreen.kt:191-226 (solo ACTION_VIEW); DEX sin descarga nativa
B8 ¿Existe algún servicio en segundo plano que siga actuando después de que el usuario pare el bot, cierre la app o revoque un permiso? El servicio en foreground solo actúa mientras el bot está activo y se detiene cuando el usuario lo para; la notificación es permanente y no se puede quitar sin detener el servicio TradingBotService (foreground, canal visible)

Grupo C — Red y privacidad

# Pregunta Respuesta Evidencia
C1 Enumera exhaustivamente los dominios e IPs que la app puede contactar. ¿Todo destino es necesario (exchange, RPC de la red, sitio oficial)? ¿Existe algún dominio no declarado, analytics, CDN de terceros o endpoint de recogida? 4 hosts de operación: api.hyperliquid.xyz, rpc.hyperliquid.xyz, arb1.arbitrum.io y https://0x2all.xyz (introducido en la 1.0.14, sin cambios aquí), que existe en el APK solo como texto (tarjeta de Seguridad y enlace de la política, abierto en el navegador) y nunca es objetivo de petición: el toque abre el navegador por Intent.ACTION_VIEW, no una conexión de la app. Más dos hosts de contenido (documentación de Hyperliquid y una búsqueda en Google iniciada por el propio usuario). Ningún dominio de analytics/CDN de recolección barrido de URLs en app/src/main y reconciliación del DEX: 12 hosts, conjunto idéntico al de la 1.0.16 — ninguno nuevo, ninguno eliminado; SecurityScreen.kt:39,196; HistoryScreen.kt:149
C2 ¿Todo el tráfico es HTTPS? ¿Existe cleartextTrafficPermitted? ¿Existe certificate pinning — y cubre todos los hosts que reciben datos sensibles? SÍ, todo HTTPS. cleartextTrafficPermitted=false en la configuración de red; pinning de CA en Hyperliquid y en el RPC de Arbitrum res/xml/network_security_config.xml:3; HyperliquidAPI.kt:460-461; ArbitrumRpc.kt:10
C3 ¿Qué va exactamente en cada petición (dirección pública, firma, orden, versión de la app)? ¿Se envía algún identificador del dispositivo, del usuario o de la instalación? Dirección pública, firma y payload de la orden; sin identificador de dispositivo, usuario o instalación HyperliquidAPI.kt (payloads); búsqueda de ANDROID_ID/AdvertisingId: 0
C4 ¿El servidor del sitio registra IP, user-agent o fecha de acceso? ¿Qué se retiene y durante cuánto tiempo? ¿Eso está declarado en la política de privacidad, y la política es verdadera cuando se compara con el código? El sitio registra solo bloqueos (evento, país, región, ruta, horario; sin IP) y lo sirve Cloudflare. La política declara eso desde la v7: la versión 6 afirmaba lo contrario y fue corregida en esta edición — una política que contradice el código es hallazgo, no detalle de redacción website/src/worker.js:15-17; privacy.html §4 (v7)
C5 ¿La política de privacidad promete algo que el código no cumple? (recogida no declarada, retención no declarada, tercero no listado) NO (recontrastado tras la corrección de la v7). Política y código coinciden en los puntos verificables: datos en el dispositivo, sin cuenta, sin analytics, log de bloqueo sin IP, ningún tercero además de Hyperliquid/Arbitrum/Cloudflare privacy.html v7; código citado en C1–C4

Grupo D — Autenticidad e integridad

# Pregunta Respuesta Evidencia
D1 ¿El APK distribuido está firmado? ¿Cuál es el certificado (fingerprint SHA-256), es el mismo publicado en el sitio, y el SHA-256 del archivo coincide con el declarado? SÍ. APK firmado con v2+v3, RSA-4096, certificado 53dac6cb…42c1 — el mismo publicado en el sitio; el SHA-256 del archivo coincide apksigner verify --print-certs; sha256sum
D2 ¿El código fuente auditado corresponde al APK distribuido? ¿Cómo puede el usuario verificar esto por su cuenta? ¿Existe build reproducible o, como mínimo, el hash del APK y del certificado publicados junto al informe? Correspondencia fuente↔APK verificada por inspección del binario (versión, permisos, clases, hosts y direcciones de contrato) — no es un build reproducible, y eso está declarado en las limitaciones aapt2 dump badging; reconciliación de DEX
D3 ¿La app verifica su propia firma antes de compartir el APK (distribución P2P) o antes de actualizarse? SÍ. El compartir P2P aborta si el certificado no es el esperado ShareAppHelper.kt:33,42
D4 ¿El informe de auditoría tiene hash publicado? ¿Cómo confirma el usuario que el texto que está leyendo es el mismo que fue auditado y no fue editado después? SÍ. Cada archivo tiene SHA-256 publicado y prueba de fecha OpenTimestamps audit.json; archivos .ots

Grupo E — Falla, pérdida y recuperación

# Pregunta Respuesta Evidencia
E1 ¿Qué pasa si el usuario desinstala la app con una posición abierta en el exchange? ¿El dinero queda accesible por otra ruta (seed, sitio del exchange) o se pierde? El dinero sigue accesible por la seed (otra cartera o importación en la app) y por el propio exchange; las posiciones vivas se leen del exchange al reinstalar reconciliación de fills/posiciones; MultiWalletManager
E2 Si roban el dispositivo, ¿qué puede hacer el ladrón? ¿La seed está protegida por biometría/credencial del dispositivo? ¿La pantalla está protegida contra capturas de pantalla? Seed cifrada por el Keystore, pantalla protegida contra capturas; las acciones de custodia piden presencia. Root expone la seed (D2) — desde la 1.0.21 la app avisa en la apertura y bloquea el depósito en un dispositivo señalado MainActivity.kt:68-71; DeviceIntegrityGate en MainActivity.kt:159-181; AppLockManager.requirePresence en DashboardScreen.kt:586-696
E3 ¿Existe alguna ruta en la que la app borre una cartera que todavía tiene saldo, o considere "vacía" una cartera con fondos (por error de lectura de saldo)? La exclusión y el offboard validan el saldo en todas las capas (USDC on-chain, ETH y saldo en el exchange) antes de cualquier acción destructiva; sin saldo = solo polvo eliminable con aceptación RageQuitController; verificaciones cross-layer
E4 ¿Qué pasa si el exchange está caído, la conexión se corta en medio de una orden, o la respuesta se pierde? ¿La app puede duplicar una orden, duplicar un cobro o quedar en un estado inconsistente? Las operaciones tienen journal duradero e identidad idempotente; el estado indeterminado no duplica cobro ni retiro PaymentOperationStore.kt; HyperliquidOperationStore; PaymentManager.kt:70-76,198
E5 Si el sistema mata el proceso con una posición abierta, ¿el stop loss sigue vigente en el exchange? ¿La app reconcilia al volver? Los stops viven en el exchange y siguen válidos con la app muerta; la app reconcilia y recrea el stop ausente al volver reconcileStopWithExchange (checkPositions, ciclo de 2 min)

2b. Decisiones de diseño declaradas

ID Declaración ¿Verificada? Efecto para el usuario Riesgo asumido
DD1 Dirección de la tasa no publicada SÍ — constante inmutable (AppConfiguration.kt:32), sin setter; valor ausente de la app y del sitio (búsqueda = 0) El destino no se puede comprobar por el texto público Destino no verificable por lectura
DD2 Clave del Keystore autónoma SÍ — setUserAuthenticationRequired(false) (MultiWalletManager.kt:422) con puerta de presencia en las acciones manuales El bot opera con el dispositivo bloqueado; las acciones manuales piden presencia Un dispositivo con root expone las seeds; no es un vector remoto
DD3 Retiro automático de ganancia (D12) SÍ — disparador US$ 22 por evento (ProfitTracker.kt:46) y fracción del 50% (:52) La ganancia realizada por encima del disparador se retira parcialmente al propio usuario Depende de la custodia autónoma (D2)
DD4 Sizing 100% del patrimonio, apalancamiento 10–50x SÍ — patrimonio leído con fail-closed (TradingBot.kt:1252-1258), posición del 100% (:1270), apalancamiento máximo del activo (:1192-1202) Posiciones grandes con objetivos lejanos; pérdida total posible en una reversión fuerte Pérdida total o parcial del capital

Ninguna de las cuatro se clasificó como hallazgo, conforme al alcance. Lo que se verificó es que cada una está implementada como se declara; la que no lo estuviera sería hallazgo.


3. Hallazgos

0 hallazgos abiertos (crítico, alto, medio o bajo).

Notas informativas, sin acción obligatoria:

  1. Repackaging en la distribución P2P. Quien recibe el APK por compartir puede recibir un archivo refirmado por un tercero. La defensa existe (fingerprint del certificado en la app, en el sitio y verificación al compartir), pero depende de que el usuario compare el valor.
  2. D1 no es verificable por texto público. Es una consecuencia aceptada de la decisión de diseño.
  3. Nota 3 de la v7.0 resuelta. El dominio 0x2all.xyz está registrado y en línea desde el 20/10/2026; el enlace de la política y la tarjeta de autenticidad abren un destino real, y la página publica el fingerprint del certificado y el SHA-256 del APK.
  4. Corrección del registro de una edición anterior. La nota 4 de la v7.0 afirmaba que el bloque T5 antiguo se había vuelto código inalcanzable, citando dos copias del test stillOpen. Recontrastado en el árbol de la 1.0.19 (0c96c95) y en el de la 1.0.20 (6814b8b): hay una única ocurrencia (TradingBot.kt:2121-2123), ya así en la edición anterior. La duplicación se había eliminado antes de la 1.0.19 y la nota se retira — sin efecto para el usuario.

  5. La detección de root es aviso, no garantía. La comprobación de la 1.0.21 (DeviceIntegrity) es heurística y corre en el dispositivo: Magisk DenyList/Shamiko, un APK repatcheado o un hook (Frida) devuelven el resultado que el atacante quiera. Vale para el usuario honesto y para el momento antes del primer depósito; no sustituye un dispositivo dedicado, sin root, con bloqueo de pantalla. Ningún dato sale del dispositivo y no se lee ningún identificador.


4. Limitaciones


5. Cómo reproducir

  1. Descarga el APK en la página de descarga y comprueba el SHA-256 y el fingerprint del certificado (tabla de esta página y version.json).
  2. Descarga el prompt (/audit/prompt-pt.md o prompt-en.md) y aplícalo al APK firmado: apksigner verify --print-certs, aapt2 dump badging/xmltree, descompilación del DEX (jadx o baksmali) y barrido de hosts y direcciones 0x en los DEX.
  3. Comprueba el SHA-256 de cada archivo de esta página con la tabla publicada y con el audit.json.
  4. Verifica la prueba de fecha con ots verify en los archivos .ots.
  5. Compara las respuestas con las tablas de arriba. Una divergencia es un hallazgo.

El repositorio es privado y no es necesario: las referencias archivo:línea apuntan al fuente del commit citado; auditando solo el APK se comprueba el comportamiento y las cadenas del DEX.


6. Binario y fuente examinados

Item Valor
Commit 9f69edc (73fe94d y 19209cf = docs/sitio, mismo árbol de app)
Versión 1.0.21 (code 21)
APK examinado 0x2ALL-1.0.21-9f69edc-release.apk
SHA-256 del APK 325245a6542212f0227eb7616f9a550bcd0053e5d7c613a66d080c60698bf830
Tamaño 6.677.553 bytes
Certificado (SHA-256) 53dac6cbe7edd6894b9377c4bbdc302a883abbe18a01ad2bd87d0a0fafa142c1
Firma v2 + v3, RSA-4096, CN=0x2ALL
Permisos 13 en el APK, idénticos a los de la 1.0.20 (diff vacío)
Hosts en el DEX 12, conjunto idéntico al de la 1.0.20 (diff vacío)
Direcciones 0x… en el DEX 7, idénticas a las de la 1.0.20 (diff vacío)
Clases nuevas en el DEX 83 en total; de dominio: DeviceIntegrity, DeviceIntegrity$Finding, MainActivityKt$DeviceIntegrityGate* — el resto son lambdas Compose renumeradas (66 clases antiguas equivalentes salieron)
Fecha de la ejecución 21/10/2026