Sur les trois plateformes, j’avais retrouvé toutes les jonctions natives : le point d’entrée de l’enregistrement de l’appareil, la couche où le SDK de sécurité intercepte la requête, l’intercepteur natif qui réécrit le paquet sortant, ainsi que les méthodes enregistrées dynamiquement dans le runtime via RegisterNatives. Le script Frida était attaché, les logs défilaient et chaque hook se déclenchait de façon fiable. Puis j’ai ouvert le catalogue de commandes : nombre d’endpoints d’application natifs appelables pour ces trois plateformes, zéro.
Dans le même projet, une autre plateforme en comptait 11. Ils étaient validés sur le terrain, intégrés au code et disponibles comme commandes à part entière.
La différence ne tient pas à la technique de hooking. Tous les hooks fonctionnaient et le schéma des liaisons était proprement établi. Ce qui change tout, c’est un seuil dont beaucoup ne soupçonnent pas l’existence. Entre un hook qui se déclenche et une capacité réellement exploitable, il y a un niveau de preuve à franchir. Cet article explique comment le définir, pourquoi déclarer une fonction « pas encore exploitable » coûte moins cher que publier un endpoint inachevé, et quel rôle le modèle joue dans ce processus.
Un hook actif ne fait pas un endpoint exploitable
En reverse d’application, on a facilement tendance à voir le message « le hook a marché » comme la ligne d’arrivée. Le script s’est attaché à la méthode cible, les arguments apparaissent dans les logs, tout comme la valeur de retour et la pile d’appels. Cette impression d’être enfin à l’intérieur est grisante — et trompeuse.
Or, le catalogue de commandes ne se contente pas de savoir que vous êtes à l’intérieur. Il exige autre chose : à partir d’une entrée normale, cette commande peut-elle produire de manière fiable une charge utile non vide, correctement structurée et directement utilisable en aval ? Entre les deux, le chemin est long.
Le scénario classique est le suivant : tous les hooks se déclenchent, la chaîne est complète, vous voyez même la requête d’enregistrement de l’appareil partir dans les logs, le SDK de sécurité calculer sa valeur, puis l’intercepteur natif ajouter l’en-tête de signature. Tout paraît correct. Mais sur un appareil réel, l’enregistrement renvoie un ID d’appareil nul, ou l’endpoint de détail retourne un corps vide. La chaîne est ouverte, mais les données sont absentes.
Que possède-t-on alors ? Des sondes capables d’observer le comportement interne d’une version donnée de l’application. Ce que l’on n’a pas, c’est un endpoint appelable. Présenter le premier comme s’il s’agissait du second, c’est laisser un piège aux personnes qui passeront après vous.
Le groupe témoin : pourquoi 11 commandes d’un côté, 0 de l’autre
Il suffit de mettre les deux ensembles de plateformes face à face pour voir apparaître le seuil.
Plateforme témoin (une application de communauté vidéo) | Trois grandes applications de contenu | |
|---|---|---|
Chaîne de hooks | Identifiée et validée sur le terrain | Toutes identifiées, tous les hooks se déclenchent |
État d’installation réel | Identité d’appareil non nulle obtenue | ID d’appareil nul / profil d’appareil réel absent |
Réponse non vide | Détail structuré | Détail vide / corps vide |
Dans le catalogue de commandes | 11 | 0 |
Les 11 commandes de la plateforme témoin ne reposent pas sur de « meilleurs hooks ». Chacune a franchi les quatre seuils de preuve décrits dans la section suivante avant de devenir une commande Python de premier rang, avec ses éléments de validation structurés.
Les trois autres plateformes n’étaient pas laissées de côté. La couche d’interception du SDK de sécurité, l’intercepteur de requêtes natif et le lot de méthodes enregistrées dynamiquement par JNI avaient tous été compris, et les hooks Frida restent en place. Mais tant que l’état d’installation réel ne passe pas, tant que l’enregistrement de l’appareil ne fournit pas une identité non nulle, tout ce qui suit demeure vide. Le nombre de commandes natives reste donc honnêtement à 0 ; ce qui est actuellement utilisable passe par une voie distincte, via le transport Web ou le navigateur.
À l’échelle du bilan, ce lot de capacités mobiles en compte 32 : 23 ont réellement abouti à des implémentations, tandis que les 9 restantes sont bloquées à l’étape « en attente d’un profil d’appareil réel ». Aucune n’a été ajoutée prématurément au catalogue. Le chiffre 9 n’est pas un échec ; il mesure précisément ce qui est « compris au niveau de la chaîne, mais insuffisamment prouvé ».
Les quatre seuils de preuve avant publication
En décomposant cette comparaison, une capacité applicative doit satisfaire quatre conditions avant d’entrer dans le catalogue de commandes. S’il en manque une seule, elle n’y entre pas.
Premier seuil : un état d’installation réel. La requête doit provenir d’une identité d’installation non nulle, reconnue par l’amont. Un hook déclenché dans un environnement émulé ou sur un profil cassé ne suffit pas. Si l’enregistrement de l’appareil renvoie un ID nul, ce seuil échoue ; dès lors, peu importe que la chaîne soit complète, la sortie restera vide. C’est ici que les trois plateformes se sont retrouvées bloquées.
Deuxième seuil : une réponse non vide. Une requête qui passe n’est pas forcément une requête qui ramène des données. Elle part, retourne un code 200, mais son corps est vide. Cette « réponse vide réussie » est plus dangereuse qu’une erreur, car elle échappe à tous les contrôles qui vérifient uniquement l’absence d’exception. Le seuil impose des données structurées, complètes et non vides, prêtes à être consommées en aval.
Troisième seuil : une classification complète des erreurs. Lorsqu’une capacité mature échoue, elle doit indiquer la cause, pas lancer une erreur générique. Page absente, utilisateur non connecté, runtime non prêt, réponse vide : ces cas échouent pour des raisons très différentes et doivent correspondre à des codes d’erreur distincts, plutôt qu’être confondus. Une commande capable de dire seulement « ça a échoué » n’est pas prête pour le catalogue : l’appelant ne peut pas savoir s’il doit réessayer, se réauthentifier ou ignorer le résultat.
Quatrième seuil : un test fermé et reproductible. Un succès isolé ne constitue pas une capacité. Avec la même entrée et le même parcours, l’exécution doit réussir de façon répétée, et ce succès doit être figé dans des preuves de validation structurées et conservées. Si cela fonctionne une fois puis renvoie du vide à l’essai suivant, vous ne maîtrisez pas encore la chaîne : vous êtes simplement tombé une fois sur le bon état du runtime.
Lorsque les quatre seuils sont franchis, la capacité peut rejoindre le catalogue de commandes. Si un seul reste ouvert, elle demeure un actif de recherche, pas un endpoint. Toute la différence de cet article tient dans l’écart entre ces deux termes.
Face à une trace Frida massive, répartir l’analyse entre plusieurs modèles
Pour déterminer sur lequel de ces quatre seuils une chaîne bloque, la matière première est la trace produite par Frida. Et une trace Frida gonfle très vite. Attachez quelques dizaines de méthodes, exécutez une chaîne complète : des dizaines ou des centaines de milliers de lignes sont normales, dont l’essentiel est du bruit issu de polyfills, de heartbeats ou de modules métier sans rapport.
Envoyer l’ensemble à un seul modèle en demandant « pourquoi ce hook ne récupère-t-il pas de données ? » produit une supposition générale. Plus le contexte est vaste, plus le modèle risque de relier deux segments d’appels qui n’ont rien à voir. La bonne méthode consiste à découper la trace, puis à confier les différents segments à des niveaux de modèles adaptés à chaque tâche.
C’est un cas d’école pour combiner plusieurs niveaux, plutôt que de choisir le modèle le plus puissant pour tout faire. Les quatre tâches demandent des aptitudes très différentes :
Étape de traitement des logs Frida | Compétence requise | Choix | model id |
|---|---|---|---|
Lire d’un coup la trace d’une chaîne d’appels complète | Contexte long, lecture de toute la chaîne en une fois | Kimi K3 |
|
Étiqueter des dizaines de milliers de lignes (enregistrement appareil / réseau / crypto / bruit) | Économique, milliers d’appels avec forte concurrence | Claude Sonnet 5 |
|
Déterminer sur lequel des quatre seuils une chaîne est bloquée | Raisonnement solide, capable de trancher | Claude Opus 5 |
|
Comparer le point de divergence de deux traces et expliquer une réponse vide | Attribution avec raisonnement intermédiaire, explication à partir de lignes précises | GPT-5.6 Sol |
|
L’étape d’étiquetage mérite une attention particulière. C’est un travail de tri pur : écarter, parmi des dizaines de milliers de lignes, le bruit et ne conserver que les classes liées à l’enregistrement de l’appareil, à la cryptographie et au réseau. Le volume est trop élevé pour un traitement manuel. Ce type de « jugement simple à très haute fréquence » correspond exactement au terrain des modèles économiques ; le confier à un modèle de raisonnement serait un gaspillage direct. Une fois le log réduit à quelques centaines de lignes étiquetées, le modèle de raisonnement peut identifier le seuil en cause. Coût et précision se retrouvent alors alignés.
Pour constater l’effet de cette répartition, faites un essai :
Prélevez un segment de trace d’une chaîne, de quelques milliers à quelques dizaines de milliers de lignes.
Étiquetez-le par morceaux avec
claude-sonnet-5, en écartant le bruit et en gardant les classes d’enregistrement de l’appareil, de cryptographie et de réseau.Envoyez le résumé étiqueté à
claude-opus-5et demandez-lui d’indiquer « lequel des quatre seuils bloque actuellement cette chaîne, ainsi que les lignes qui étayent ce diagnostic ».À titre de contrôle, donnez la même trace brute entière à un seul modèle avec la même question.
Observez un seul point : obtient-on une hypothèse générale, ou un seuil précis accompagné de lignes précises ? C’est ce qui doit guider votre sélection.
Un hook est une sonde d’observation liée à une version, pas un signataire
Pourquoi conserver les hooks de ces trois plateformes tout en les tenant fermement hors du catalogue de commandes ? Parce qu’un hook est une sonde, pas un signataire : ce sont deux objets de nature fondamentalement différente.
Un hook est lié à une build précise de l’application, par exemple la version 32.x. Les symboles, offsets et agencements de méthodes dont il dépend appartiennent tous à cette version. Dès que l’éditeur publie une mise à jour, tout peut bouger et le hook cesse immédiatement de fonctionner. Il est, par essence, périssable et dépendant d’une version. Il répond à une question d’observation : « que fait cette version en interne, à cet instant ? » C’est exactement la fonction d’un outil d’instrumentation : la documentation Frida présente Interceptor comme un moyen d’observer et de réécrire des appels à l’exécution. Il permet de voir comment une fonction est appelée et quels arguments elle reçoit, mais il ne constitue pas en lui-même un livrable qui « calcule une signature à partir d’une entrée ».
Un signataire destiné au catalogue d’endpoints doit faire l’inverse : être stable, reproductible, autonome et intégrable en CI. Il répond à une question de capacité réutilisable : « donnez-moi une entrée et je calcule la bonne signature ». Même si un hook expose clairement l’ossature de l’algorithme de signature, on n’en est encore qu’à l’étape d’identification de l’algorithme, loin du travail de purification et de vérification différentielle nécessaire pour obtenir un signataire autonome.
Cela explique aussi l’ordre de l’échelle de purification : une capacité doit d’abord tenter une connexion directe en Python pur, puis se replier sur l’exécution locale d’un fragment minimal de signature dans Node/V8, et n’accepter qu’à contrecœur un pont navigateur passif. Un hook n’est même pas encore sur cette échelle. Il se situe avant elle, dans la phase de recherche, pas dans celle de l’implémentation. Enregistrer une sonde d’observation liée à une version comme signataire revient à coller l’étiquette « implémentation » sur un travail de recherche inachevé.
Archiver un actif de recherche sans le laisser se périmer
Exclure un hook du catalogue d’endpoints ne signifie pas le jeter. Il représente un véritable investissement de recherche, avec des connaissances sur la chaîne, des échantillons et des éléments de validation. Le supprimer serait une perte sèche. L’enjeu est de l’archiver correctement ; sinon, dans deux mois, même son auteur ne saura plus jusqu’où le travail était réellement arrivé.
Un actif de recherche sur une application doit au minimum documenter ces quatre éléments :
Type de transport. Indiquez si la chaîne utilise le protocole natif, une signature locale Node/V8 ou un pont navigateur passif. Cela détermine jusqu’où elle pourra être purifiée par la suite.
Niveau de preuve. Précisez combien des quatre seuils ont été franchis : « chaîne identifiée », « état d’installation non nul obtenu mais réponse vide », ou encore « réponse non vide mais non reproductible ». Cette donnée indique immédiatement à la personne suivante ce qui manque pour rendre la capacité exploitable.
Version / build de l’application. Notez la version à laquelle le hook est lié. Sans cela, lors d’une mise à jour en amont, il devient impossible de savoir si l’erreur vient de votre implémentation ou d’un changement de build.
Résumé des échantillons. Conservez une copie désensibilisée de l’entrée et de la sortie observées lors de l’exécution. C’est le point d’ancrage le plus rapide pour reprendre la recherche.
Avec ces quatre informations, une chaîne bloquée à 0 commande reste un actif sur lequel avancer, et non un tas de logs périmés. Cela évite aussi les deux pires réflexes : supprimer le hook en faisant comme si la recherche n’avait jamais eu lieu, ou le forcer dans le catalogue en prétendant qu’il est utilisable. Le second choix coûte particulièrement cher. Un endpoint qui « semble appelable mais renvoie en réalité du vide » transfère son coût à tous les consommateurs qui font confiance au catalogue : ils construisent leur intégration dessus, ajoutent des mécanismes de retry, se font piéger une fois par les données vides et finissent par ne plus croire au catalogue entier. Une lacune assumée, marquée « actif de recherche, 0 commande », ne vous coûte qu’à vous, une fois, dans une ligne de README.
Une coquille vide coûte plus cher qu’une absence, et c’est particulièrement évident ici.
Une seule clé pour fluidifier le découpage des logs
Revenons à la répartition des logs de la quatrième section : le contexte long pour lire une trace complète, le niveau économique pour l’étiquetage en masse, le niveau de raisonnement pour la classification, et le niveau de raisonnement intermédiaire pour l’attribution. Quatre niveaux issus de plusieurs fournisseurs, donc quatre SDK, quatre modes d’authentification et quatre formats d’erreur. Pour analyser une trace Frida avec quatre clients, la plupart des équipes font rapidement le calcul, jugent l’effort disproportionné, puis finissent par confier des centaines de milliers de lignes à un seul modèle — soit en brûlant leur budget, soit sans parvenir à traiter le volume.
AIReiter supprime cette friction : une clé, une interface compatible OpenAI, les quatre niveaux derrière, et un simple changement du champ model dans le corps de la requête pour passer de l’un à l’autre.
# Étiqueter le log : niveau économique, milliers d’appels à forte concurrence
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": "<one chunk of trace + labeling instruction>"}]
}'
# Classifier le seuil : passer au niveau de raisonnement, ne rien changer d’autre
# "model": "claude-opus-5"
# Lire toute la chaîne d’appels : niveau à contexte long
# "model": "kimi-k3"
# Attribuer la différence de réponse :
# "model": "gpt-5.6-sol"
Si vous utilisez déjà le SDK OpenAI, pointez base_url vers https://aireiter.com/api/v1 sans rien modifier d’autre. Avec le SDK Anthropic, utilisez POST /api/v1/messages avec la même clé.
La structure de coûts de cette approche est déséquilibrée, et la réduction s’applique précisément là où le volume est le plus fort. La trace d’une seule chaîne compte de dizaines à des centaines de milliers de lignes ; l’étiquetage se fait morceau par morceau et une plateforme représente des centaines ou des milliers d’appels à claude-sonnet-5. C’est là que se concentre le gros du coût. Les appels à contexte long, qui lisent une trace entière, atteignent quelques centaines de milliers de tokens par entrée : autre poste important. La classification et l’attribution nécessitent peu d’appels, à un coût unitaire plus élevé. La réduction de 30 % sur Claude s’applique donc directement à l’étiquetage et à la classification des seuils, les deux étapes les plus coûteuses. GPT à moitié prix couvre le niveau d’attribution. La lecture à contexte long tourne sur Kimi K3, accessible avec la même clé.
Essayer sans inscription : donnez un segment de trace à
claude-sonnet-5pour l’étiqueter, puis demandez àclaude-opus-5de le classifier. Vous verrez s’il identifie directement le seuil de blocage avant de décider de l’intégrer à votre flux.
Pour conclure
En rétro-ingénierie d’applications, un hook qui se déclenche donne une capacité d’observation : « je peux voir ce que cette version fait en interne ». Le catalogue d’endpoints attend une capacité d’appel : « donnez-moi une entrée et je produis de façon fiable des données correctes et non vides ». Entre les deux se trouvent quatre seuils de preuve : un état d’installation réel, une réponse non vide, la classification des erreurs et un test reproductible.
Trois plateformes entièrement cartographiées, tous les hooks actifs, mais toujours 0 commande : ce n’est pas un échec, c’est de la rigueur. L’état d’installation réel ne passe pas, on s’arrête donc honnêtement à 0, on archive le hook comme actif de recherche et on reprend dès que le profil d’appareil est disponible.
La place du modèle dans ce flux est bien définie. Il découpe, étiquette, classe et attribue pour vous des centaines de milliers de lignes de trace, réduisant une après-midi de lecture de logs à quelques minutes pour identifier le point de blocage d’une chaîne. Mais le verdict final — « le seuil est-il franchi ? » — ne revient pas au modèle, pas plus que le jugement « l’hypothèse est-elle correcte ? » dans les tests différentiels. Il dépend des quatre critères que vous avez fixés et des preuves qui se répètent. Pour voir comment placer le modèle au bon endroit dans l’ensemble du workflow de reverse engineering, consultez la vue d’ensemble en quatre étapes.