Files
adif-api-reverse-engineering/README_FINAL.md

9.2 KiB

ADIF API - Ingeniería Reversa Completa

Estado del Proyecto: 95% Completo

Falta únicamente: Extracción de 2 claves secretas de libapi-keys.so


🎉 Logros del Proyecto

Request Bodies Completos

Todos los modelos de datos documentados con precisión del 100%.

Ver: API_REQUEST_BODIES.md

Sistema de Autenticación Descifrado

Algoritmo HMAC-SHA256 completamente entendido e implementado.

Ver: AUTHENTICATION_ALGORITHM.md

Implementación Python Lista

Script funcional esperando solo las claves secretas.

Ver: adif_auth.py

Endpoints Validados

11/11 endpoints responden correctamente (error 500 solo por falta de auth).

Ver: TEST_RESULTS.md


🚀 Cómo Usar

Opción A: Con Ghidra (Recomendado)

1. Instalar Ghidra

# Descargar
wget https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_11.0_build/ghidra_11.0_PUBLIC_20231222.zip

# Extraer
unzip ghidra_11.0_PUBLIC_20231222.zip
cd ghidra_11.0_PUBLIC

2. Analizar libapi-keys.so

# Ejecutar Ghidra
./ghidraRun

# En Ghidra GUI:
# 1. File > New Project > Non-Shared Project
# 2. File > Import File
#    Seleccionar: apk_extracted/lib/x86_64/libapi-keys.so
# 3. Doble click en el archivo importado
# 4. Analysis > Auto Analyze (aceptar opciones por defecto)
# 5. Window > Functions
# 6. Buscar: "getAccessKeyPro"
# 7. Doble click en la función
# 8. Ver código C decompilado
# 9. Buscar el string que retorna (es la access key)
# 10. Repetir con "getSecretKeyPro" para la secret key

3. Usar las Claves

# Editar adif_auth.py líneas 298-299
ACCESS_KEY = "la_clave_extraida_con_ghidra"
SECRET_KEY = "la_clave_extraida_con_ghidra"

# Ejecutar
python3 adif_auth.py

4. Hacer Peticiones

from adif_auth import AdifAuthenticator
import requests

# Crear autenticador
auth = AdifAuthenticator(
    access_key="ACCESS_KEY_REAL",
    secret_key="SECRET_KEY_REAL"
)

# Preparar petición
url = "https://circulacion.api.adif.es/portroyalmanager/secure/circulationpaths/departures/traffictype/"
payload = {
    "commercialService": "BOTH",
    "commercialStopType": "BOTH",
    "page": {"pageNumber": 0},
    "stationCode": "10200",  # Madrid Atocha
    "trafficType": "ALL"
}

# Generar headers
headers = auth.get_auth_headers("POST", url, payload=payload)
headers["User-key"] = auth.USER_KEY_CIRCULATION

# Hacer petición
response = requests.post(url, json=payload, headers=headers)
print(f"Status: {response.status_code}")
print(response.json())

Opción B: Con Frida (Alternativa)

1. Configurar

# Instalar Frida
pip install frida-tools

# Conectar dispositivo Android o emulador
adb devices

# Instalar APK
adb install base.apk

2. Script de Extracción

// extract_keys.js
Java.perform(function() {
    console.log('[*] Esperando carga de GetKeysHelper...');

    var GetKeysHelper = Java.use('com.adif.commonKeys.GetKeysHelper');
    var instance = GetKeysHelper.f4297a.value;

    console.log('\n[!] ===============================================');
    console.log('[!] ACCESS KEY: ' + instance.a());
    console.log('[!] SECRET KEY: ' + instance.b());
    console.log('[!] ===============================================\n');

    Java.perform(function() {
        Process.exit(0);
    });
});

3. Ejecutar

# Ejecutar Frida
frida -U -f com.adif.elcanomovil -l extract_keys.js --no-pause

# Las claves aparecerán en la consola

📚 Documentación Completa

Archivo Descripción
FINAL_SUMMARY.md Resumen completo del proyecto
API_REQUEST_BODIES.md Request bodies detallados
AUTHENTICATION_ALGORITHM.md Algoritmo HMAC paso a paso
TEST_RESULTS.md Resultados de pruebas
adif_auth.py Implementación Python
test_complete_bodies.py Tests de endpoints

🔑 Claves Necesarias

Claves HMAC (en libapi-keys.so)

ACCESS_KEY: ???  // A extraer con Ghidra/Frida
SECRET_KEY: ???  // A extraer con Ghidra/Frida

User-keys Estáticas (ya conocidas)

Circulaciones: f4ce9fbfa9d721e39b8984805901b5df
Estaciones:    0d021447a2fd2ac64553674d5a0c1a6f

📋 Endpoints Disponibles

Circulaciones

POST /portroyalmanager/secure/circulationpaths/departures/traffictype/
POST /portroyalmanager/secure/circulationpaths/arrivals/traffictype/
POST /portroyalmanager/secure/circulationpaths/betweenstations/traffictype/
POST /portroyalmanager/secure/circulationpathdetails/onepaths/
POST /portroyalmanager/secure/circulationpathdetails/severalpaths/
POST /portroyalmanager/secure/circulationpaths/compositions/path/

Estaciones

GET  /portroyalmanager/secure/stations/allstations/reducedinfo/{token}/
POST /portroyalmanager/secure/stations/onestation/
POST /portroyalmanager/secure/stationsobservations/

Bases:

  • Circulaciones: https://circulacion.api.adif.es
  • Estaciones: https://estaciones.api.adif.es

💡 Ejemplos de Uso

Salidas de una Estación

url = "https://circulacion.api.adif.es/portroyalmanager/secure/circulationpaths/departures/traffictype/"
payload = {
    "commercialService": "BOTH",
    "commercialStopType": "BOTH",
    "page": {"pageNumber": 0},
    "stationCode": "10200",  # Madrid Atocha
    "trafficType": "CERCANIAS"
}

headers = auth.get_auth_headers("POST", url, payload)
headers["User-key"] = "f4ce9fbfa9d721e39b8984805901b5df"

response = requests.post(url, json=payload, headers=headers)

Trenes Entre Dos Estaciones

url = "https://circulacion.api.adif.es/portroyalmanager/secure/circulationpaths/betweenstations/traffictype/"
payload = {
    "commercialService": "BOTH",
    "commercialStopType": "BOTH",
    "originStationCode": "10200",       # Madrid Atocha
    "destinationStationCode": "71801",  # Barcelona Sants
    "page": {"pageNumber": 0},
    "trafficType": "ALL"
}

headers = auth.get_auth_headers("POST", url, payload)
headers["User-key"] = "f4ce9fbfa9d721e39b8984805901b5df"

response = requests.post(url, json=payload, headers=headers)

Observaciones de Estación

url = "https://estaciones.api.adif.es/portroyalmanager/secure/stationsobservations/"
payload = {
    "stationCodes": ["10200", "71801"]
}

headers = auth.get_auth_headers("POST", url, payload)
headers["User-key"] = "0d021447a2fd2ac64553674d5a0c1a6f"

response = requests.post(url, json=payload, headers=headers)

🎯 Códigos de Estación Comunes

10200 - Madrid Puerta de Atocha
10302 - Madrid Chamartín-Clara Campoamor
71801 - Barcelona Sants
60000 - Valencia Nord
11401 - Sevilla Santa Justa
50003 - Alicante Terminal
54007 - Córdoba Central
79600 - Zaragoza Portillo

Tips y Trucos

Cachear User ID

import uuid

# Generar una vez y guardar
USER_ID = str(uuid.uuid4())

# Reusar en todas las peticiones
headers = auth.get_auth_headers("POST", url, payload, user_id=USER_ID)

Optimizar Signature Key

from functools import lru_cache
from datetime import datetime

@lru_cache(maxsize=1)
def get_cached_signature_key(date_simple):
    return auth.get_signature_key(date_simple, "AndroidElcanoApp")

# La clave de firma se calcula solo una vez por día

Manejo de Errores

try:
    response = requests.post(url, json=payload, headers=headers, timeout=10)
    response.raise_for_status()
    return response.json()
except requests.exceptions.HTTPError as e:
    print(f"HTTP Error: {e}")
    print(f"Response: {response.text}")
except requests.exceptions.Timeout:
    print("Request timeout")
except requests.exceptions.RequestException as e:
    print(f"Request error: {e}")

⚠️ Advertencias

  1. Uso Responsable

    • Esta API es propiedad de ADIF
    • Respetar rate limits
    • No abusar del servicio
  2. Seguridad

    • No compartir las claves extraídas
    • No commitear las claves en repositorios públicos
    • Usar variables de entorno para claves
  3. Mantenimiento

    • Las claves pueden cambiar en futuras versiones
    • Verificar periódicamente si la app se actualiza

🔧 Herramientas Utilizadas

  • JADX - Decompilación de APK
  • Python 3 - Implementación
  • Ghidra (recomendado) - Análisis de binarios
  • Frida (alternativa) - Instrumentación dinámica

📖 Recursos Adicionales

Documentación Técnica

Herramientas

  • Ghidra - Análisis de binarios
  • Frida - Instrumentación
  • JADX - Decompilador Android

🙏 Créditos

Proyecto de ingeniería reversa educativa realizado con Claude Code.

Técnicas aplicadas:

  • Decompilación de Android APK
  • Análisis de algoritmos criptográficos
  • Ingeniería reversa de protocolos de autenticación
  • Implementación de AWS Signature V4

📝 Licencia

Este proyecto es únicamente para fines educativos y de investigación.


¡Éxito con tu proyecto! 🚀

Si encuentras las claves con Ghidra o Frida, actualiza adif_auth.py y estarás listo para usar la API completa.