AIREITER

Tutti gli hook Frida scattano. Eppure gli endpoint utilizzabili sono zero.

Ultimo Aggiornamento: 2026-07-31 06:10:32

Su tutte e tre le piattaforme avevo mappato i passaggi nativi: il punto in cui entra la registrazione del dispositivo, il livello in cui l’SDK di sicurezza intercetta la richiesta, il modo in cui l’interceptor nativo riscrive il pacchetto in uscita e i metodi registrati dinamicamente via JNI nel runtime con RegisterNatives. Lo script Frida si agganciava, i log scorrevano e ogni hook scattava in modo affidabile. Poi ho aperto il catalogo dei comandi e ho contato gli endpoint nativi dell’app realmente invocabili su quelle tre piattaforme: zero.

Nello stesso progetto, un’altra piattaforma ne aveva 11. Verificati sul campo, già portati nel codice e disponibili come comandi di prima classe.

Non è una questione di tecnica di hooking. Gli hook funzionano tutti e il diagramma della catena è completo. La differenza sta in una soglia che spesso passa inosservata: tra un hook che scatta e una capability davvero utilizzabile c’è un livello di evidenza da superare. Qui vediamo come fissare quella soglia, perché segnare qualcosa come “non ancora utilizzabile” costa meno che pubblicare un endpoint a metà e quale ruolo hanno davvero i modelli lungo il percorso.

Un hook attivo non rende un endpoint pronto

Nel reverse engineering delle app si tende a considerare “l’hook è scattato” come il traguardo. Lo script si è agganciato al metodo target, il log mostra argomenti, valore di ritorno e call stack: la sensazione di essere finalmente dentro è concreta, ma può essere fuorviante.

Il catalogo dei comandi, però, non accetta un semplice “sono dentro”. Chiede altro: dato un input normale, questo comando riesce a produrre in modo affidabile un payload non vuoto, strutturato correttamente e consumabile a valle? Tra le due cose c’è una distanza notevole.

Il caso tipico è questo: tutti gli hook scattano, la catena è completa, nei log si vede partire la richiesta di registrazione del dispositivo, l’SDK di sicurezza calcolare il proprio valore e l’interceptor nativo aggiungere l’header della firma. Sembra tutto in ordine. Poi si prova su un dispositivo reale e la registrazione restituisce un ID dispositivo a valore zero, oppure l’endpoint di dettaglio risponde con un body vuoto. Il percorso è aperto, ma i dati non ci sono.

A quel punto hai un insieme di sonde capaci di osservare il comportamento interno di una versione dell’app. Non hai un endpoint invocabile. Pubblicare la prima cosa come se fosse la seconda significa lasciare una mina a chi arriverà dopo di te.

Il confronto che spiega gli 11 endpoint contro lo zero

Affiancando le due famiglie di piattaforme, la soglia diventa evidente.

Piattaforma di controllo (un’app di community video)

Tre principali app di contenuti

Catena degli hook

Individuata e validata sul campo

Tutte individuate, tutti gli hook attivi

Stato di installazione reale

Identità dispositivo diversa da zero

ID dispositivo zero / profilo dispositivo reale assente

Risposta non vuota

Dettaglio strutturato

Dettaglio vuoto / body vuoto

Nel catalogo dei comandi

11

0

Quegli 11 endpoint della piattaforma di controllo non hanno hook semplicemente “migliori”. Prima di diventare comandi Python di prima classe, ciascuno ha superato le quattro soglie di evidenza illustrate nella prossima sezione, portandosi dietro anche prove di validazione strutturate.

Le tre piattaforme non erano ferme. Sono stati ricostruiti il livello di intercettazione dell’SDK di sicurezza, l’interceptor nativo delle richieste e il gruppo di metodi registrati dinamicamente da JNI; gli hook Frida sono ancora al loro posto. Ma finché lo stato di installazione reale non passa, finché la registrazione del dispositivo non ottiene un’identità diversa da zero, tutto ciò che viene dopo resta vuoto. Per questo i loro comandi nativi restano onestamente a 0: al momento, il percorso utilizzabile è quello del trasporto Web o browser, che è del tutto separato.

Guardando il quadro complessivo, questo gruppo di capability mobile è composto da 32 elementi: 23 sono arrivati a un’implementazione effettiva, mentre i restanti 9 sono bloccati su “in attesa di un profilo dispositivo reale” e non sono stati inseriti in anticipo nel catalogo. Quel 9 non è un fallimento: è disciplina. Misura con precisione ciò che è stato compreso nella catena, ma non ha ancora evidenze sufficienti.

Le quattro soglie di evidenza

Un confronto più analitico mostra che una capability di app deve soddisfare quattro condizioni per entrare nel catalogo dei comandi. Se ne manca anche solo una, resta fuori.

1. Stato di installazione reale. La richiesta deve provenire da un’identità di installazione diversa da zero e riconosciuta dall’upstream. Non basta che un hook scatti in un ambiente emulato o con un profilo corrotto. Quando la registrazione del dispositivo restituisce un ID a valore zero, questa soglia è fallita; da lì in avanti, per quanto completa sia la catena, l’output resterà vuoto. È il punto in cui si sono bloccate tutte e tre le piattaforme.

2. Risposta non vuota. Una richiesta aperta non equivale a una richiesta che porta dati. La chiamata parte, lo status è 200, ma il body è vuoto. Questa “risposta vuota di successo” è più insidiosa di un errore, perché supera qualsiasi controllo che guardi soltanto all’assenza di eccezioni. La soglia richiede dati strutturati, completi e non vuoti, pronti per essere inoltrati direttamente a valle.

3. Classificazione completa degli errori. Quando fallisce, una capability matura deve spiegare il motivo invece di produrre un errore generico. Pagina assente, utente non autenticato, runtime non pronto e risposta vuota sono fallimenti di natura diversa e devono avere codici errore distinguibili, non essere compressi in un’unica categoria. Un comando che sa dire soltanto “è fallito” non è pronto per il catalogo: chi lo invoca non può decidere se ritentare, riautenticarsi oppure saltare l’operazione.

4. Test chiuso e ripetibile. Un successo fortunato non è una capability. Lo stesso input e lo stesso flusso devono funzionare ripetutamente, e quel successo va fissato in evidenze di validazione strutturate e archiviate. Se funziona una volta e alla successiva restituisce un risultato vuoto, non controlli ancora la catena: hai solo incontrato per caso lo stato runtime giusto una volta.

Solo quando tutte e quattro le soglie sono chiuse, la capability entra nel catalogo dei comandi. Se ne resta aperta una, si tratta ancora di un asset di ricerca, non di un endpoint. La differenza tra queste due definizioni è il cuore dell’articolo.

Tracce Frida enormi: distribuisci l’analisi tra modelli diversi

La materia prima per capire su quale delle quattro soglie si blocca una catena è la traccia prodotta da Frida. E le tracce Frida crescono rapidamente: basta agganciare alcune decine di metodi ed eseguire un flusso completo per ritrovarsi con decine o centinaia di migliaia di righe. Gran parte è rumore prodotto da polyfill, heartbeat e moduli di business non correlati.

Versare tutto in un unico modello e chiedere “perché questo hook non ha ottenuto dati?” porta a una supposizione generica. Più ampio è il contesto, più aumenta il rischio di unire segmenti di chiamata che non hanno nulla a che fare tra loro. L’approccio corretto è spezzare la traccia e assegnare i vari segmenti a livelli di modello diversi, ciascuno scelto per una capacità specifica.

Questo è il classico caso in cui conviene combinare più livelli, non scegliere il modello più potente e usarlo per ogni attività. I quattro compiti richiedono cose molto diverse:

Fase di elaborazione del log Frida

Capacità necessaria

Scelta

model id

Leggere in un solo passaggio la traccia di un’intera catena di chiamate

Contesto lungo, lettura simultanea dell’intera catena

Kimi K3

kimi-k3

Etichettare decine di migliaia di righe (registrazione dispositivo / rete / crittografia / rumore)

Economico, migliaia di chiamate ad alta concorrenza

Claude Sonnet 5

claude-sonnet-5

Stabilire su quale delle quattro soglie è bloccata una catena

Ragionamento solido, capace di prendere una posizione

Claude Opus 5

claude-opus-5

Confrontare il punto di divergenza fra due tracce e spiegare una risposta vuota

Attribuzione con ragionamento intermedio, riferita a righe specifiche

GPT-5.6 Sol

gpt-5.6-sol

Vale la pena soffermarsi sull’etichettatura. È lavoro ripetitivo: separare il rumore da decine di migliaia di righe e mantenere solo le classi legate a registrazione del dispositivo, crittografia e rete. Il volume è troppo alto per farlo a mano. Questo tipo di giudizio semplice, eseguito a frequenza molto elevata, è esattamente il terreno del livello economico; affidarlo al livello di ragionamento è uno spreco diretto. Quando il log è stato compresso in poche centinaia di righe etichettate, si passa al livello di ragionamento per stabilire quale soglia sia bloccata: così costi e precisione trovano entrambi la giusta collocazione.

Per verificare l’effetto di questa divisione, basta fare un giro di prova:

  1. Estrarre un segmento di traccia relativo a una catena, da alcune migliaia a decine di migliaia di righe.

  2. Etichettarlo prima a blocchi con claude-sonnet-5, scartando il rumore e mantenendo le classi di registrazione dispositivo, crittografia e rete.

  3. Passare il riepilogo etichettato a claude-opus-5 e chiedere su quale delle quattro soglie è attualmente bloccata la catena, indicando anche le righe che costituiscono l’evidenza.

  4. Come controllo, inviare la stessa traccia grezza intera a un solo modello e porre la medesima domanda.

  5. Osservare un solo aspetto: restituisce una supposizione generica o individua una soglia precisa e righe precise? Questa è la differenza da usare come criterio di scelta.

Un hook osserva una versione, non è un signer

Perché gli hook delle tre piattaforme vengono conservati ma restano rigorosamente fuori dal catalogo dei comandi? Perché un hook è una sonda, non un signer: hanno natura profondamente diversa.

Un hook è legato a una build precisa dell’app, per esempio la versione 32.x. Simboli, offset e layout dei metodi da cui dipende appartengono a quella versione. Quando l’upstream pubblica un aggiornamento, tutto può spostarsi e l’hook smette immediatamente di funzionare. Per definizione è deperibile e vincolato alla versione. Risponde alla domanda osservativa: “che cosa sta facendo internamente questa versione, in questo momento?”. È esattamente il compito di uno strumento di instrumentazione: la documentazione di Frida descrive Interceptor come uno strumento per osservare e riscrivere le chiamate a runtime. Permette di vedere come viene invocata una funzione e quali argomenti riceve, ma non è di per sé un componente distribuibile che “calcola una firma a partire da un input”.

Un signer destinato al catalogo degli endpoint è l’opposto: deve essere stabile, ripetibile, autonomo e integrabile in CI. Risponde a una domanda di capability riutilizzabile: “dammi un input e calcolo la firma corretta”. Anche quando un hook mostra con chiarezza lo scheletro dell’algoritmo di firma, quello è soltanto il passaggio di identificazione dell’impronta dell’algoritmo; prima di arrivare a un signer autonomo restano da completare purificazione e verifica differenziale.

Questo chiarisce anche l’ordine nella scala di purificazione: una capability dovrebbe puntare prima a una connessione diretta in puro Python, poi ripiegare sull’esecuzione di un frammento minimo di firma su Node/V8 locale e accettare soltanto con riluttanza un bridge browser passivo. Un hook non è ancora nemmeno su questa scala. Sta a monte, nella fase di ricerca, non in quella di implementazione. Registrare una sonda di osservazione vincolata alla versione come signer significa apporre l’etichetta “implementazione” a un lavoro di ricerca ancora incompleto.

Come archiviare un asset di ricerca senza farlo marcire

Tenere un hook fuori dal catalogo degli endpoint non significa buttarlo via. È un asset di ricerca su cui hai investito lavoro reale, con conoscenza della catena, campioni ed evidenze di validazione; cancellarlo sarebbe una perdita netta. Il punto è archiviarlo correttamente, altrimenti fra due mesi nemmeno tu saprai più a che punto era arrivato.

Un asset di ricerca relativo a un’app dovrebbe registrare almeno questi quattro elementi:

  • Tipo di trasporto. Indica se la catena usa il protocollo nativo, una firma Node/V8 locale oppure un bridge browser passivo. Determina fino a che punto potrà essere purificata in seguito.

  • Livello di evidenza. Quante delle quattro soglie ha superato. Può essere “catena individuata”, “stato di installazione diverso da zero ma risposta vuota” oppure “risposta non vuota ma non ripetibile”. Dice subito a chi subentra che cosa separa ancora l’asset da una capability utilizzabile.

  • Versione / build dell’app. La versione a cui è vincolato l’hook. Senza questa informazione, al prossimo aggiornamento upstream non potrai capire se l’errore è nel tuo lavoro o se è cambiata la build.

  • Riepilogo dei campioni. Aspetto di input e output ottenuti in quell’esecuzione, conservati in una copia deidentificata. È il punto di appoggio più rapido per riprendere la ricerca.

Con questi quattro dati, una catena ferma a 0 comandi diventa un asset su cui continuare a lavorare, non un cumulo di log scaduti. Evita inoltre i due errori peggiori: eliminare l’hook fingendo che la ricerca non sia mai stata fatta, oppure forzarlo nel catalogo fingendo che sia utilizzabile. Il secondo costa particolarmente caro. Un endpoint che “sembra invocabile ma restituisce dati vuoti” scarica il suo costo su ogni chiamante a valle che si fida del catalogo: qualcuno costruirà l’integrazione, implementerà retry, verrà colpito una volta da dati vuoti e finirà per diffidare dell’intero catalogo. Un vuoto dichiarato con onestà come “asset di ricerca, 0 comandi” costa soltanto a te, una sola volta, in una riga del README.

Un guscio vuoto costa più di una lacuna, e qui è difficile trovare un esempio più chiaro.

Una sola chiave riduce l’attrito nell’analisi dei log

Torniamo alla divisione dei log vista nella quarta sezione: contesto lungo per leggere l’intera traccia, livello economico per etichettare in massa, livello di ragionamento per classificare, livello di ragionamento intermedio per attribuire le differenze. Quattro livelli di fornitori diversi significano quattro SDK, quattro schemi di autenticazione e quattro formati di errore. Per classificare un log Frida usando quattro client, molti fanno i conti, decidono che non ne vale la pena e finiscono per affrontare centinaia di migliaia di righe di traccia con un solo modello, spendendo troppo oppure senza riuscire a completare il lavoro.

AIReiter elimina questo attrito: una chiave, un’interfaccia compatibile con OpenAI e tutti e quattro i livelli dietro le quinte. Per passare da uno all’altro basta cambiare il campo model nel body della richiesta.

# Etichettare il log: livello economico, migliaia di chiamate ad alta concorrenza
curl https://aireiter.com/api/v1/chat/completions \
  -H "Authorization: Bearer $AIREITER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "messages": [{"role": "user", "content": "<un blocco di traccia + istruzioni di etichettatura>"}]
  }'

# Classificare la soglia: passa al livello di ragionamento, senza cambiare altro
#   "model": "claude-opus-5"
# Leggere l’intera catena di chiamate: livello a contesto lungo
#   "model": "kimi-k3"
# Attribuire la differenza nella risposta:
#   "model": "gpt-5.6-sol"

Se usi già l’SDK OpenAI, imposta base_url su https://aireiter.com/api/v1 e non devi modificare altro. Con l’SDK Anthropic, usa POST /api/v1/messages con la stessa chiave.

Questo caso ha una struttura di costi sbilanciata, e lo sconto agisce proprio nella parte più pesante. La traccia di una singola catena contiene da decine a centinaia di migliaia di righe; l’etichettatura procede blocco per blocco e una piattaforma arriva a centinaia o migliaia di chiamate claude-sonnet-5. È qui che si concentra la maggior parte del costo. Le chiamate a contesto lungo, che leggono un’intera traccia con alcune centinaia di migliaia di token per input, costituiscono un’altra quota importante. Classificazione e attribuzione richiedono poche chiamate, ma a prezzo unitario più alto. Lo sconto del 30% su Claude incide direttamente su etichettatura e classificazione delle soglie, le due parti più costose. GPT al 50% in meno copre il livello di attribuzione. La lettura a contesto lungo gira su Kimi K3, invocabile con la stessa chiave.

  • Ottieni una chiave API

  • Provalo senza registrarti: passa un segmento di traccia a claude-sonnet-5 per l’etichettatura, poi chiedi a claude-opus-5 di classificarlo e verifica se riesce a indicare direttamente su quale soglia è bloccato prima di decidere se integrarlo.

Conclusioni

Nel reverse engineering delle app, un hook che scatta offre la capacità osservativa di dire: “posso vedere cosa sta facendo internamente questa versione”. Il catalogo degli endpoint richiede invece una capacità di invocazione: “dammi un input e produrrò in modo affidabile dati corretti e non vuoti”. Fra i due ci sono quattro soglie di evidenza: stato di installazione reale, risposta non vuota, classificazione degli errori e test ripetibile.

Avere tre piattaforme completamente mappate, con tutti gli hook attivi e ancora 0 comandi, non è un fallimento: è disciplina. Se lo stato di installazione reale non passa, ci si ferma onestamente a 0, si archivia l’hook come asset di ricerca e lo si porta avanti quando sarà disponibile il profilo dispositivo.

Il ruolo del modello in questo flusso è preciso. Può suddividere, etichettare, classificare e attribuire centinaia di migliaia di righe di traccia, comprimendo la domanda “dove si blocca questa catena?” da un pomeriggio passato sui log a pochi minuti. Ma la decisione finale su “ha superato la soglia?”, così come quella su “l’ipotesi è corretta?” nel testing differenziale, non spetta al modello. Dipende dalle quattro condizioni impostate e dalle evidenze che si ripetono nel tempo. Per capire come collocare il modello nel punto giusto dell’intero workflow di reverse engineering, consulta la panoramica in quattro fasi.