Esta actualización reorganiza el proyecto de reverse engineering de la API de ADIF con los siguientes cambios: Estructura del proyecto: - Movida documentación principal a carpeta docs/ - Consolidados archivos markdown redundantes en CLAUDE.md (contexto completo del proyecto) - Organización de tests en carpeta tests/ con README explicativo - APK renombrado de base.apk a adif.apk para mayor claridad Archivos de código: - Movidos adif_auth.py y adif_client.py a la raíz (antes en api_testing_scripts/) - Eliminados scripts de testing obsoletos y scripts de Frida no utilizados - Nuevos tests detallados: test_endpoints_detailed.py y test_onepaths_with_real_trains.py Descubrimientos: - Documentados nuevos hallazgos en docs/NEW_DISCOVERIES.md - Actualización de onePaths funcionando con commercialNumber real (devuelve 200) - Extraídos 1587 códigos de estación en station_codes.txt Configuración: - Actualizado .gitignore con mejores patrones para Python e IDEs - Eliminados archivos temporales de depuración y logs
8.2 KiB
ADIF API - Reverse Engineering ✅
Cliente Python completo para acceder a la API de ADIF (El Cano Móvil) mediante ingeniería reversa.
Estado del Proyecto: ✅ COMPLETADO CON ÉXITO Autenticación HMAC-SHA256 implementada, 4/8 endpoints funcionales, 1587 códigos de estación extraídos.
🚀 Inicio Rápido
# Instalar dependencias
pip install requests
# Ejecutar demo
python3 adif_client.py
Uso Básico
from adif_client import AdifClient
# Inicializar cliente
client = AdifClient(
access_key="and20210615",
secret_key="Jthjtr946RTt"
)
# Obtener salidas de Madrid Atocha
trains = client.get_departures("10200", "AVLDMD")
for train in trains:
info = train['commercialPathInfo']
print(f"Tren {info['commercialPathKey']['commercialCirculationKey']['commercialNumber']}")
# Obtener ruta completa de un tren
route = client.get_train_route(
commercial_number="03194",
launching_date=1764889200000,
origin_station_code="10200",
destination_station_code="71801"
)
📊 Estado del Proyecto
✅ Funcionalidades Implementadas
| Característica | Estado | Descripción |
|---|---|---|
| Extracción de claves | ✅ | Claves extraídas de libapi-keys.so con Ghidra |
| Algoritmo HMAC-SHA256 | ✅ | Implementación completa y validada |
| Códigos de estación | ✅ | 1587 estaciones extraídas |
| Endpoints funcionales | ✅ | 4/8 endpoints (50%) |
| Cliente Python | ✅ | API completa y lista para usar |
| Documentación | ✅ | Completa en /docs |
📍 Endpoints Disponibles
✅ Funcionales (4/8)
| Método | Endpoint | Descripción |
|---|---|---|
get_departures() |
/departures/traffictype/ |
Salidas de una estación |
get_arrivals() |
/arrivals/traffictype/ |
Llegadas a una estación |
get_train_route() |
/onepaths/ |
Ruta completa de un tren |
get_station_observations() |
/stationsobservations/ |
Observaciones de estaciones |
❌ Bloqueados por Permisos (4/8)
/betweenstations/traffictype/- 401 Unauthorized/onestation/- 401 Unauthorized/severalpaths/- 401 Unauthorized/compositions/path/- 401 Unauthorized
Nota: Los endpoints bloqueados tienen implementación correcta pero las claves no tienen permisos suficientes.
📁 Estructura del Proyecto
adif-api-reverse-engineering/
├── 📄 README.md # Este archivo
├── 📄 LICENSE # Licencia MIT
│
├── 🐍 Python Scripts (Core)
│ ├── adif_auth.py # ⭐ Implementación HMAC-SHA256
│ ├── adif_client.py # ⭐ Cliente completo de la API
│ ├── query_api.py # CLI interactivo
│ └── generate_curl.py # Generador de curls
│
├── 📊 Datos
│ ├── station_codes.txt # ⭐ 1587 códigos de estación
│ └── extracted_keys.txt # Claves extraídas
│
├── 🧪 Tests
│ ├── test_endpoints_detailed.py # Test exhaustivo con debug
│ └── test_onepaths_with_real_trains.py # Test con datos reales
│
├── 📚 Documentación (/docs)
│ ├── FINAL_STATUS_REPORT.md # Informe completo
│ ├── API_DOCUMENTATION.md # Documentación de API
│ ├── AUTHENTICATION_ALGORITHM.md # Algoritmo HMAC
│ ├── ENDPOINTS_ANALYSIS.md # Análisis de endpoints
│ ├── API_REQUEST_BODIES.md # Payloads documentados
│ ├── GHIDRA_GUIDE.md # Tutorial de Ghidra
│ ├── NEW_DISCOVERIES.md # Últimos descubrimientos
│ └── CLAUDE.md # Contexto del proyecto
│
├── 📦 APK & Análisis
│ ├── base.apk # APK original
│ ├── apk_decompiled/ # Código decompilado (JADX)
│ ├── apk_extracted/ # APK extraído
│ │ ├── assets/stations_all.json # Fuente de estaciones
│ │ └── lib/x86_64/libapi-keys.so # Librería con claves
│ └── frida_scripts/ # Scripts de análisis dinámico
│
└── 🗂️ Otros
├── archived_tests/ # Tests antiguos archivados
└── api_testing_scripts/ # Scripts auxiliares
🔑 Autenticación
Claves Extraídas
ACCESS_KEY = "and20210615"
SECRET_KEY = "Jthjtr946RTt"
USER_KEY_CIRCULATION = "f4ce9fbfa9d721e39b8984805901b5df"
USER_KEY_STATIONS = "0d021447a2fd2ac64553674d5a0c1a6f"
Fuente: apk_extracted/lib/x86_64/libapi-keys.so (Ghidra)
Algoritmo HMAC-SHA256
Implementación basada en AWS Signature v4:
⚠️ CRÍTICO: El orden de headers NO es alfabético:
canonical_headers = (
f"content-type:application/json\n"
f"x-elcano-host:{host}\n" # ← NO alfabético
f"x-elcano-client:api-elcano\n"
f"x-elcano-date:{timestamp}\n"
f"x-elcano-userid:{user_id}\n"
)
Ver adif_auth.py para implementación completa.
🗺️ Códigos de Estación
Total: 1587 estaciones
Archivo: station_codes.txt
Formato: código TAB nombre TAB tipos_tráfico
Top 10 Estaciones
10200 Madrid Puerta de Atocha AVLDMD
10302 Madrid Chamartín-Clara Campoamor AVLDMD
71801 Barcelona Sants AVLDMD,CERCANIAS
60000 València Nord AVLDMD
11401 Sevilla Santa Justa AVLDMD
50003 Alacant Terminal AVLDMD,CERCANIAS
54007 Córdoba Central AVLDMD
79600 Zaragoza Portillo AVLDMD,CERCANIAS
03216 València J.Sorolla AVLDMD
04040 Zaragoza Delicias AVLDMD,CERCANIAS
💡 Casos de Uso
1. Monitor de Retrasos
import time
from adif_client import AdifClient
client = AdifClient(ACCESS_KEY, SECRET_KEY)
while True:
trains = client.get_departures("10200", "ALL")
for train in trains:
passthrough = train.get('passthroughStep', {})
dep_sides = passthrough.get('departurePassthroughStepSides', {})
delay = dep_sides.get('forecastedOrAuditedDelay', 0)
if delay > 300: # Más de 5 minutos
print(f"⚠️ Retraso de {delay//60} min")
time.sleep(30)
2. Consultar Rutas Completas
# Obtener trenes con sus rutas
trains_with_routes = client.get_all_departures_with_routes(
station_code="10200",
traffic_type="AVLDMD",
max_trains=5
)
for train in trains_with_routes:
print(f"🚄 Tren {train['commercial_number']}")
print(f" Paradas: {len(train['route'])}")
3. CLI Interactivo
python3 query_api.py
🔬 Herramientas Utilizadas
- Ghidra - Extracción de claves de
libapi-keys.so - JADX - Decompilación del APK
- Python 3 - Implementación del cliente
- Frida (opcional) - Análisis dinámico
📖 Documentación
Toda la documentación está en /docs:
- FINAL_STATUS_REPORT.md - Informe completo del proyecto
- API_DOCUMENTATION.md - Documentación de la API
- AUTHENTICATION_ALGORITHM.md - Algoritmo HMAC detallado
- GHIDRA_GUIDE.md - Tutorial paso a paso
🎯 Logros del Proyecto
✅ Claves de autenticación extraídas con Ghidra ✅ Algoritmo HMAC-SHA256 implementado y validado ✅ 1587 códigos de estación disponibles ✅ 4/8 endpoints funcionales (50%) ✅ Cliente Python listo para producción ✅ Documentación completa
⚠️ Limitaciones
- 4/8 endpoints bloqueados por permisos del servidor
- Las claves extraídas son de perfil "anónimo/básico"
- No hay acceso a información de usuario autenticado
📄 Licencia
MIT License - Ver LICENSE
⚠️ Disclaimer: Proyecto con fines educativos y de investigación. Úsalo de forma responsable.
✨ Créditos
- ADIF - Por la aplicación El Cano Móvil
- Ghidra & JADX - Herramientas de reverse engineering
- Comunidad de seguridad - Por compartir conocimiento
Última actualización: 2025-12-05 Estado: ✅ Proyecto completado con éxito