Skip to content

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 :

TechnologieUsage typique
WebAssembly (.wasm)Calculs intensifs, logique compilée depuis C/C++/Rust
Native MessagingCommunication entre une extension de navigateur et un binaire local
SharedArrayBuffer + WorkersParallélisme mémoire partagée
EmscriptenPortage de bibliothèques C/C++ vers le navigateur

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.

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 WASM
WebAssembly.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.

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 source
Login.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 opaque
var _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"

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))
)

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ée
instance.exports.checkPassword(1337);
// → 1

Pour une analyse approfondie, télécharger le fichier .wasm et le passer dans un décompilateur :

Terminal window
# Convertir en WAT lisible
wasm2wat module.wasm -o module.wat
# Décompiler vers du pseudo-C avec wasm-decompile
wasm-decompile module.wasm -o module.dcmp

Outils : wabt (WebAssembly Binary Toolkit), Ghidra avec le plugin WASM, Binary Ninja.

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/false selon 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

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.