Code natif embarqué dans le JavaScript
JavaScript est un langage sandboxé : il ne peut pas accéder directement aux ressources système, aux ports réseau de la machine hôte, ou aux API matérielles. Pour contourner ces limites ou pour déléguer des traitements intensifs, certaines applications embarquent du code natif aux côtés du JavaScript.
Pourquoi du code natif dans une application web
Section titled “Pourquoi du code natif dans une application web”Deux raisons principales poussent les développeurs à sortir du JavaScript pur :
Performance (CPU-bound) — JavaScript n’est pas adapté aux calculs lourds. Encoder ou décoder des flux volumineux, exécuter des algorithmes de chiffrement complexes, ou traiter de la vidéo en temps réel se font plus efficacement dans un module compilé.
Accès système — JavaScript, par construction, ne peut pas parler directement à un port de la machine cliente, lire le système de fichiers local, ou interagir avec du matériel. Du code natif via WebAssembly ou un module de navigateur peut ouvrir ces accès.
Les technologies les plus rencontrées :
| Technologie | Usage typique |
|---|---|
| WebAssembly (.wasm) | Calculs intensifs, logique compilée depuis C/C++/Rust |
| Native Messaging | Communication entre une extension de navigateur et un binaire local |
| SharedArrayBuffer + Workers | Parallélisme mémoire partagée |
| Emscripten | Portage de bibliothèques C/C++ vers le navigateur |
Ce que ça change pour un audit
Section titled “Ce que ça change pour un audit”Du code natif dans une application web signifie que la logique sensible n’est plus lisible directement dans le JavaScript. L’authentification, le chiffrement, ou la vérification de licence peuvent être compilés dans un binaire .wasm que le JS appelle comme une boîte noire.
C’est aussi un indicateur que le développeur a voulu rendre cette partie difficile à inspecter.
Détecter la présence de code natif
Section titled “Détecter la présence de code natif”Repérer les fichiers .wasm dans l’onglet Réseau
Section titled “Repérer les fichiers .wasm dans l’onglet Réseau”Ouvrir les DevTools (F12), onglet Network, filtrer par type Wasm ou rechercher .wasm dans les requêtes. Un fichier présent confirme l’usage de WebAssembly.
Identifier les imports WebAssembly dans le JavaScript
Section titled “Identifier les imports WebAssembly dans le JavaScript”Chercher dans les sources les patterns d’instanciation :
// Pattern courant de chargement d'un module WASMWebAssembly.instantiateStreaming(fetch('module.wasm'), importObject) .then(result => { const exports = result.instance.exports; // exports contient les fonctions du module natif });Les fonctions exportées par le module sont appelables depuis le JavaScript. Leur nom apparaît dans exports.
Inspecter le contenu via toString()
Section titled “Inspecter le contenu via toString()”Pour les fonctions JavaScript standard, .toString() retourne le code source complet de la fonction. Sur du code qui semble opaque ou obfusqué, c’est le premier réflexe à avoir depuis la console :
// Identifier la fonction suspecte et afficher son code sourceLogin.toString()checkLicense.toString()verifyToken.toString()Sur une fonction native (implémentée dans le moteur du navigateur), le retour sera :
function alert() { [native code] }Sur une fonction JavaScript ordinaire, même obfusquée, le code source complet s’affiche — le moteur ne peut pas exécuter ce qu’il n’t pas décodé.
// Exemple : fonction obfusquée apparemment opaquevar _0x3f2a = function(_0x1b, _0x2c) { return _0x1b == "\x61\x64\x6d\x69\x6e" && _0x2c == "\x70\x61\x73\x73"; }
// toString() retourne le source complet avec les séquences d'échappement_0x3f2a.toString()// → "function(_0x1b, _0x2c) { return _0x1b == "\x61\x64\x6d\x69\x6e" && _0x2c == "\x70\x61\x73\x73"; }"
// Décoder les séquences hexadécimales"\x61\x64\x6d\x69\x6e" // → "admin""\x70\x61\x73\x73" // → "pass"Inspecter un module WebAssembly
Section titled “Inspecter un module WebAssembly”Un fichier .wasm est du bytecode binaire. Le lire directement est difficile, mais plusieurs approches permettent d’en extraire la logique.
Convertir en texte WAT depuis les DevTools
Section titled “Convertir en texte WAT depuis les DevTools”Chrome et Firefox affichent le module WebAssembly en format texte (WAT — WebAssembly Text Format) dans l’onglet Sources. Naviguer jusqu’au fichier .wasm : le navigateur le désassemble automatiquement.
;; Exemple de représentation WAT d'une vérification de mot de passe(module (func $checkPassword (param $input i32) (result i32) local.get $input i32.const 1337 i32.eq ) (export "checkPassword" (func $checkPassword)))Inspecter les exports depuis la console
Section titled “Inspecter les exports depuis la console”Une fois le module instancié, lister les fonctions exposées au JavaScript :
// Après WebAssembly.instantiate(...)console.log(Object.keys(instance.exports));// → ["checkPassword", "encryptData", "validateLicense"]
// Appeler directement une fonction exportéeinstance.exports.checkPassword(1337);// → 1Décompiler avec des outils dédiés
Section titled “Décompiler avec des outils dédiés”Pour une analyse approfondie, télécharger le fichier .wasm et le passer dans un décompilateur :
# Convertir en WAT lisiblewasm2wat module.wasm -o module.wat
# Décompiler vers du pseudo-C avec wasm-decompilewasm-decompile module.wasm -o module.dcmpOutils : wabt (WebAssembly Binary Toolkit), Ghidra avec le plugin WASM, Binary Ninja.
Ce qu’on cherche dans le code natif
Section titled “Ce qu’on cherche dans le code natif”Après inspection du module ou décodage des fonctions obfusquées, cibler :
- Logique de vérification de licence ou d’abonnement : une fonction qui retourne
true/falseselon un état interne - Credentials ou clés embarqués dans les constantes du module
- Endpoints ou URLs construites dans le code natif et jamais visibles dans le JS
- Algorithmes de chiffrement maison : souvent plus faibles que les standards
À retenir
Section titled “À retenir”Compiler du code en WebAssembly ralentit l’analyse mais ne l’empêche pas. Le navigateur doit exécuter ce code — il peut donc être observé, intercepté, et décompilé. .toString() est le premier outil à essayer sur toute fonction suspecte avant d’aller plus loin.