altstore no podía iniciar sesión. apple devolvía una página html.
un informe de la rotura del inicio de sesión de altstore en septiembre de 2026: cómo un error de "los datos no están en el formato correcto" resultó ser una página html 503 entregada a un lector de plist, cómo mis propios reintentos me dieron una respuesta equivocada y cómo el arreglo terminó siendo el cambio de una cadena de 39 caracteres dentro de binarios firmados en macos y windows.
fue un esfuerzo comunitario en altstoreio/AltStore#1699. los arreglos oficiales que salieron los escribieron otras personas, acreditadas abajo. lo mío fue el diagnóstico, los parches binarios para quien no podía esperar un release y un segundo fallo, distinto (-80009), que introdujo el primer apaño.
resumen
- altserver no podía iniciar sesión:
NSCocoaErrorDomain 3840, "encountered unknown tag html on line 1". - capturar el tráfico de grandslam mostró que el inicio de sesión con el apple id funcionaba. la tercera petición,
apptokens, recibía una página html 503, y altsign la leía como plist. - el desencadenante era un
User-Agentviejo, de la época de mojave, escrito a mano en altsign, agravado porque los reintentos reutilizaban una misma conexión del pool. - la misma cadena vivía en el framework de macos (dos copias, una por cada arquitectura) y en
AltServer.exe(utf-16le, una copia). cambiarla por otra de la misma longitud arreglaba el inicio de sesión sin recompilar. en macos hay que volver a firmar la app. - los arreglos oficiales se fusionaron en altsign entre el 8 y el 10 de sep, y altserver 1.7.6 lo arregló para todos.
el síntoma
NSCocoaErrorDomain 3840
Debug Description: Encountered unknown tag html on line 1
AltServer could not sign in with your Apple ID. The data is not in the correct format.
suena a problema de credenciales. la gente creaba apple ids nuevos, cambiaba de vpn, de región. nada ayudaba, porque el mensaje no hablaba de su cuenta.
1. mirar el tráfico real
inyecté una dylib en el altserver original para registrar sus llamadas NSURLSession y capturé el intercambio de grandslam contra POST https://gsa.apple.com/grandslam/GsService2:
Request.o=init -> 200 text/x-xml-plist
Request.o=complete -> 200 text/x-xml-plist <-- srp auth succeeds
Request.o=apptokens -> 503 text/html <-- "503 Service Temporarily Unavailable"
apple aceptó la contraseña y los datos de anisette. solo se rechazó la tercera llamada, y volvió como página de error html. altsign no maneja un cuerpo que no sea plist, así que el html va directo a PropertyListSerialization, que falla con <html> en la línea 1. ese es todo el mensaje de error.
eso también explicó por qué nada de lo que probaba la gente importaba. reproduje el mismo resultado en mi isp de casa, en el hotspot de un iphone y en una salida de vpn en eeuu, con seis servidores de anisette distintos y dos identidades de cliente. otros reportes del hilo sumaron un apple id recién creado y windows con anisette de icloud para windows. mismo 503 siempre.
2. mi primera conclusión era falsa
añadí reintentos automáticos: seis intentos en unos siete segundos, todos 503. publiqué que el rechazo era determinista.
no lo era. cada reintento reutilizaba la misma NSURLSession, así que cada intento volvía por la misma conexión del pool, fijada al mismo backend de apple que fallaba. seis reintentos en una conexión son una muestra, no seis. @Calvin-Zikakis había descrito exactamente ese bug en AltSign#49. publiqué una corrección en el hilo, porque "apple lo ha cerrado" era lo que no debía llevarse la gente.
3. la causa: un user-agent de 2019
@FrakesH identificó el desencadenante: altsign envía
akd/1.0 CFNetwork/978.0.7 Darwin/18.7.0
un user-agent de macos mojave escrito a mano. los servidores de apple empezaron a rechazarlo en apptokens.
para comprobarlo antes de tocar ningún binario, apliqué los dos arreglos en tiempo de ejecución al altserver 1.7.2 original (build 90) con la dylib inyectada. reescribía User-Agent en las peticiones a *.apple.com y ejecutaba cada petición en una URLSession efímera nueva. el inicio de sesión y la instalación funcionaron de punta a punta. en macos 27 además necesité el arreglo de anisette de @kimziro para la etapa de AOSKit -45070 / machineID vacío.
4. parchear los binarios
una dylib no es algo que la mayoría pueda usar. la cadena de reemplazo tiene la misma longitud que la vieja, 39 caracteres, así que un cambio directo de bytes funciona sin mover nada:
akd/1.0 CFNetwork/978.0.7 Darwin/18.7.0
AuthKit/1 (com.apple.dt.Xcode/3594.4.1)
macos
la cadena no está en AltServer, sino en el framework de altsign, en ascii, dos veces: AltSign-Dynamic es un binario universal y cada arquitectura tiene su copia.
AltServer.app/Contents/Frameworks/AltSign-Dynamic.framework/Versions/A/AltSign-Dynamic
0x62920 -> x86_64 slice
0x10E210 -> arm64 slice
las dos están junto a X-MMe-Client-Info. la misma disposición vale en 1.6.2 y 1.7.2. descargué un 1.6.2 original del cdn de apple para verificarlo.
el problema es la firma de código. la app original tiene firma de developer id con hardened runtime (flags=0x10000(runtime)). editar el framework la invalida:
AltServer.app: invalid signature (code or signature have been modified)
así que el parche tiene que terminar con una re-firma ad-hoc:
APP="/Applications/AltServer.app"
F="$APP/Contents/Frameworks/AltSign-Dynamic.framework/Versions/A/AltSign-Dynamic"
cp -R "$APP" ~/Desktop/AltServer-backup.app
perl -pi -e 's/akd\/1\.0 CFNetwork\/978\.0\.7 Darwin\/18\.7\.0/AuthKit\/1 (com\.apple\.dt\.Xcode\/3594\.4\.1)/g' "$F"
codesign --force --deep --sign - "$APP"
codesign --verify --deep --strict "$APP" && echo OK
@angel-knights lo confirmó en 1.6.2 bajo macos 10.15, una combinación que no pude probar.
windows
había dicho que los usuarios de windows no tenían camino porque altserver para windows no es de código abierto. era falso: cambiar una cadena no necesita el código fuente. no tengo una máquina con windows, así que desarmé el instalador oficial (altinstaller.zip → AltInstaller.msi → cab incrustado) y leí AltServer.exe 1.7.4.0 directamente:
- el user-agent viejo está guardado en utf-16le en el desplazamiento
0x14D6A8, exactamente una aparición - está junto a
/grandslam/GsService2yviewDeveloper.action, los mismos endpoints de la captura de macos - el exe no tiene firma authenticode (directorio de certificados vacío), así que parchearlo no invalida nada
- tras el cambio sigue siendo un pe32 válido, con cero copias de la cadena vieja y una de la nueva
publiqué un script de powershell que hace copia de seguridad, se niega a correr salvo que encuentre exactamente una aparición y escribe los bytes nuevos en su sitio. @akgang-rgb y @BorbushArtem lo confirmaron en windows. la versión de @BorbushArtem era mejor que la mía: revisa las dos rutas de program files, comprueba que la longitud coincide antes de escribir y busca con una ida y vuelta por iso-8859-1 en lugar de mi bucle de bytes. mandé a la gente a la suya.
la app altstore del teléfono lleva su propia copia de altsign, así que parchear altserver arregla el inicio de sesión solo en el ordenador. @spaceace90 encontró el cambio equivalente dentro del ipa.
5. el release que no lo arregló
altserver 1.7.5 salió el 8 de sep. lo bajé del feed oficial de sparkle y lo comprobé:
AltServer.app/Contents/MacOS/AltServer
0x111070 -> x86_64 slice akd/1.0 CFNetwork/978.0.7 Darwin/18.7.0
0x27E6D0 -> arm64 slice akd/1.0 CFNetwork/978.0.7 Darwin/18.7.0
la misma cadena vieja. altsign ahora estaba enlazado de forma estática, así que el objetivo del parche había pasado del framework al ejecutable principal. y como sparkle reemplaza la app entera, actualizar deshacía en silencio el parche de cualquiera. AltSign#52, el arreglo de reutilización de conexión, se había fusionado siete horas después de cortar la 1.7.5, así que todavía no estaba en ningún release.
6. un segundo bug, causado por el apaño
algunas personas que usaron un altserver recompilado empezaron a recibir -80009 MID is invalid. es un fallo distinto: apple no acepta la identidad de la máquina.
sondeé directamente las tres llamadas de aoskit en macos 27:
retrieveOTPHeadersForDSID: -> {} (broken)
machineSerialNumber -> <real serial> (still works)
machineUDID -> <real udid> (still works)
solo una estaba rota. el arreglo de anisette para macos 27 incluido en esa build engancha solo retrieveOTPHeadersForDSID: y provisiona una identidad sintética, que en mi máquina tenía serialNumber: "0" y un deviceID aleatorio. altserver enviaba entonces ese machineID sintético junto al serial y el udid reales del mac anfitrión. la petición describía dos máquinas distintas, y apple la rechazó.
@oddharsh ya había encontrado ese hueco en anisette-fix#4, que engancha también el serial y el udid para que todo el conjunto describa una sola identidad. la medición mostró por qué hacía falta: enganchar una llamada de tres deja la identidad mitad real y mitad sintética.
cómo terminó
- el 11 de sep, dos personas reportaron que el parche había dejado de funcionar ese mismo día. lo más probable es que apple hubiera empezado a filtrar el user-agent de xcode falsificado al que convergió el hilo. @MeemeeLab reportó que cualquier
Xcodeen la información de cliente parecía provocar el 503. - en el repositorio oficial, altsign fusionó #52 (@sshane), #51 y #54 (@Calvin-Zikakis) entre el 8 y el 10 de sep, y se reportó que altserver 1.7.6 funcionaba.
- el parche manual fue un apaño de unos días, no el arreglo. el arreglo fue el release oficial.
errores míos en público
- "determinista" con seis reintentos sobre una conexión del pool. corregido en el hilo.
- "no hay camino en windows". un cambio de cadena que conserva la longitud no necesita el código fuente. lo corregí y publiqué el script.
- vincular
-22410con el bug de conexión. la lista oficial de errores documenta-22410como "failed to write to disk", y yo había mezclado un síntoma de altstore en ios con uno de altserver en el pc. lo dije cuando @kswartz26 me lo señaló.
lo que me llevo
- un mensaje de error puede describir al lector, no el problema. "no está en el formato correcto" significaba que una página de error del servidor se leyó como datos. lo primero que hay que capturar es qué llegó realmente por el cable.
- los reintentos por la misma conexión no son muestras independientes. si la conexión está fijada a un backend malo, mides ese backend seis veces.
- los parches que conservan la longitud son baratos; el trabajo real es volver a firmar. en macos, un binario parcheado con la firma rota falla antes de que tu cambio llegue a ejecutarse.
- comprueba los releases, no los supongas. la 1.7.5 parecía el arreglo y no lo era. dos desplazamientos de cadena lo demostraron en un minuto.
- las pruebas de otras personas son parte del resultado. el parche de windows y el camino de 10.15 los confirmaron usuarios con hardware que yo no tengo.
enlaces
- altstoreio/AltStore#1699, el hilo principal
- AltSign#51, #52 y #54, los arreglos oficiales fusionados
- kimziro/altserver-macos27-anisette-fix y #4