
Un obfuscateur JavaScript basé sur des techniques d’obfuscation cryptographique par indistinguabilité.
Ce projet n'atteint pas l'obfuscation d'indistinguabilité. Vous ne devriez pas utiliser ce produit pour protéger des secrets. Il existe pour dissuader la rétro-ingénierie, pas pour l'empêcher

Un obfuscateur JavaScript conçu pour rendre le code extrêmement difficile à lire et à analyser, aussi bien pour les humains que pour les LLM. Écrit en TypeScript. Il implémente des techniques issues de la recherche en obfuscation cryptographique évaluée par les pairs.
npm install
npm run build
# Basic usage
node dist/index.js input.js > output.js
# With target token budget (default: 2,000,000)
node dist/index.js --target-tokens 500000 input.js > output.js
# Minimal obfuscation (small output)
node dist/index.js --target-tokens 10000 input.js > output.js
# Maximum bloat (10M tokens)
node dist/index.js --target-tokens 10000000 input.js > output.js
# Using environment variable
INPUT_FILE=input.js node dist/index.js > output.js
# Help
node dist/index.js --help
| Option | Défaut | Description |
|---|---|---|
--target-tokens <n> | 2000000 | Taille de sortie cible en tokens. Les petites entrées sont gonflées jusqu'à cette limite. Les grandes entrées produisent moins de gonflement pour rester dans le budget. |
--help, -h | Affiche le message d'aide. |
npm run build # Compile TypeScript to dist/
npm run start # Run the obfuscator (reads input.js)
npm run test # Run the test suite
npm run obfuscate-package # Run compatibility tests against npm packages
const { obfuscate } = require('./dist/obfuscator');
const code = 'function add(a, b) { return a + b; }';
const obfuscated = obfuscate(code);
// With options
const obfuscated = obfuscate(code, { targetTokens: 500000 });
L'obfuscateur applique 20 transformations réparties en 4 phases. Chaque étape s'appuie sur la précédente.
| Ordre | Transformation | Fichier | Description |
|---|---|---|---|
| 1 | Pièges anti-débogage | transforms/antiDebug.ts | Injecte des instructions eval("debugger") et 10 à 20 boucles setInterval avec des intervalles de nombres premiers (5 s à 600 s) qui déclenchent à plusieurs reprises des points d'arrêt du débogueur. Chaque instance utilise des chaînes encodées uniques. |
| 2 | Déclencheurs de programme perforé | transforms/tripwires.ts | Intègre des vérifications cachées comparant des hachages de paramètres à des valeurs secrètes. 5 modèles de hachage (empreinte binaire, arithmétique modulaire, charCodeAt, hachage numérique, typeof+length). Déclenche une corruption silencieuse de l'état, des boucles actives ou des exceptions sur les entrées secrètes. [Paper 4] |
| 3 | Injection de bruit LPN | transforms/noiseInjection.ts | Ajoute et annule un bruit aléatoire via des chemins divisés dans les calculs arithmétiques. 6 modèles : add/sub, XOR, mul/div, double variable divisée, chaîne de hachage calculée, rotation de bits. Les valeurs intermédiaires sont dénuées de sens sans tracer l'annulation complète. [Paper 7] |
| Ordre | Transformation | Fichier | Description |
|---|---|---|---|
| 4 | Aplatissement du flux de contrôle | transforms/controlFlowFlattening.ts | Convertit les corps de fonction en machines à états while(true) { switch((_s * P) % M) { ... } } avec répartition par arithmétique modulaire — les valeurs de cas sont encodées via (stateId * multiplier) % modulus en utilisant des paramètres premiers aléatoires. [Paper 3] |
| 5 | Prédicats opaques | transforms/opaquePredicates.ts | Injecte des conditions if qui s'évaluent toujours à vrai ou à faux mais qui sont mathématiquement difficiles à prouver (par exemple, (x*x+x)%2===0). 15 formules de prédicats réparties dans les catégories arithmétique modulaire, opérations binaires et vérification de types. |
| 6 | Fonctions proxy | transforms/proxyFunctions.ts | Route tous les appels de fonction via deux répartiteurs : _fc(fn, ...args) pour les appels simples, _mc(obj, prop, ...args) pour les appels de méthode. Utilise Function.prototype.apply capturé dans une variable locale pour la résilience. |
| 7 | Épuisement de la fenêtre de contexte | transforms/contextExhaustion.ts | Enveloppe les expressions dans des ternaires profondément imbriqués avec des conditions opaques, des chaînes d'expressions void et du remplissage void conditionnel. Force les LLM à gaspiller les tokens de la fenêtre de contexte dans du bruit. |
| 8 | Fusion d'expressions à virgule | transforms/commaExpressions.ts | Réduit les instructions d'expression consécutives en une seule expression à virgule : a(); b(); return c() devient return a(), b(), c(). |
| Ordre | Passe | Fichier | Description |
|---|---|---|---|
| 9 | Passe 1 : Catalogue | passes/firstPass.ts | Parcourt l'AST et catalogue chaque identifiant, construisant une carte des globaux qui attribue à chacun un nom Unicode aléatoire de 6 à 16 caractères issu de 16 plages d'écritures (CJK, Hangul, grec, cyrillique, devanagari, thaï, arabe, katakana, etc.). |
| 10 | Passe 2 : Substitution | passes/secondPass.ts | Remplace tous les noms d'identifiants par leurs équivalents Unicode obfusqués. Encode les arguments de require() avec String.fromCharCode(...). Encode les sources d'import/export statiques comme des littéraux de chaîne échappés en Unicode. Substitue les références superClass de classes, les expressions de gabarits, les motifs de déstructuration. |
| 11 | Passe 3 : Paramètres factices | passes/thirdPass.ts | Injecte de 0 à 15 paramètres inutilisés aléatoires dans chaque déclaration et expression de fonction. Ignore les fonctions avec des paramètres de reste. Supprime tous les commentaires. |
| Ordre | Transformation | Fichier | Description |
|---|---|---|---|
| 12 | Encodage des variables globales | transforms/globalVariableEncoding.ts | Remplace les références aux variables globales (découvertes dynamiquement via globalThis + le paquet window) par eval("Name<suffix>".replace(new RegExp("<suffix>$"), "")). Les deux chaînes passent par le tableau de chaînes. |
| 13 | Encodage des clés de propriétés | transforms/propertyKeyEncoding.ts | Convertit l'accès par point en accès calculé avec des registres par portée. L'accès inter-portées fonctionne car tous les suffixes résolvent le même nom de propriété à l'exécution via .replace(). |
| 14 | Encodage des nombres | transforms/numberEncoding.ts | 11 stratégies d'encodage : décalage+addition, identité XOR, complément, division, décalages imbriqués, double-NON, modulaire, etc. Chaque instance est générée de manière unique. Ignore les clés de propriétés et les valeurs des cas switch. |
| 15 | Vérification d'intégrité | transforms/selfIntegrity.ts | Injecte 2 à 4 vérifications à l'exécution : vérification du code natif eval, intégrité de Function.prototype.toString, détection d'anomalies temporelles, validation de la structure du code. Réponses anti-altération : attente active, exception, corruption silencieuse. [Paper 10] |
| 16 | Extraction du tableau de chaînes | transforms/stringArrayExtraction.ts | Collecte toutes les chaînes dans un tableau unique avec déchiffrement XOR en chaîne (la clé de l'entrée N dépend du contenu déchiffré de l'entrée N-1) et modèles d'erreurs parcimonieux dépendants de la position (chaque caractère reçoit une clé XOR différente, avec des erreurs parcimonieuses inspirées du LPN à des positions sélectionnées). [Papers 2, 9] |
| 17 | Stubs de console | obfuscator.ts | Découvre dynamiquement toutes les méthodes console et définit chacune comme une fonction no-op. |
| 18 | Minification Terser | obfuscator.ts | Supprime les espaces et la mise en forme via terser (, ). Se replie sur une suppression basée sur des expressions régulières si terser ne peut pas analyser la sortie. |
Le code mort est injecté à plusieurs endroits avec deux stratégies de génération :
| Stratégie | Source | Description |
|---|---|---|
| Basée sur des modèles | transforms/deadCodeInjection.ts | 9 types de modèles : accumulation en boucle, construction de tableaux, manipulation d'objets, concaténation de chaînes, conditionnels imbriqués, try/catch, compte à rebours while, switch calculé, chaînes d'opérations binaires. Les modèles référencent de vraies variables de portée. |
| Basée sur la mutation | transforms/deadCodeInjection.ts | Clone de réelles instructions et les mute : échange les opérateurs au sein de groupes d'équivalence, perturbe les constantes, renomme les identifiants. Produit du code mort structurellement identique au niveau de l'AST, impossible à distinguer du code réel par la seule structure. [Paper 3] |
Points d'injection du code mort :
Plusieurs transformations s'inspirent de la recherche en obfuscation cryptographique évaluée par les pairs :
| Article | Auteurs | Technique implémentée |
|---|---|---|
| [Paper 1] On the (Im)possibility of Obfuscating Programs | Barak, Goldreich, Impagliazzo, Rudich, Sahai, Vadhan, Yang | Cas de test de fonctions inobfusquables — outil de vérification qui teste si les secrets survivent à l'obfuscation. |
| [Paper 2] Candidate iO and Functional Encryption for all Circuits | Garg, Gentry, Halevi, Raykova, Sahai, Waters | Déchiffrement de chaînes en chaîne — randomisation de style Kilian où la clé de chaque entrée dépend de la chaîne déchiffrée précédente. |
| [Paper 3] iO from the Multilinear Subgroup Elimination Assumption | Gentry, Lewko, Sahai, Waters | Code mort basé sur la mutation (structurellement identique au code réel) ; transitions d'état par arithmétique modulaire dans le CFF. |
| [Paper 4] How to Use iO: Deniable Encryption, and More | Sahai, Waters | Déclencheurs de programme perforé — vérifications cachées qui se déclenchent sur des entrées secrètes. |
| [Paper 7] iO from Well-Founded Assumptions | Jain, Lin, Sahai | Injection de bruit inspirée du LPN dans les calculs numériques. |
| [Paper 9] iO from Bilinear Maps and LPN Variants | Ragavan, Vafa, Vaikuntanathan | Encodage XOR parcimonieux avec modèles d'erreurs dépendants de la position. |
| [Paper 10] iO of Null Quantum Circuits and Applications | Bartusek, Malavolta | Test de circuit nul pour la vérification de la qualité du code mort ; vérification d'intégrité (double mode). |
Deux outils de vérification mesurent la qualité de l'obfuscation, situés dans src/verification/ :
verification/nullCircuitTest.ts)Obfusque une fonction réelle et une fonction « nulle » (même forme, ne fait rien), puis compare 14 métriques structurelles pour évaluer à quel point elles sont distinguables. Plus la similarité est élevée, meilleure est l'obfuscation.
import { runNullCircuitTest } from './verification/nullCircuitTest';
const result = runNullCircuitTest(realCode, paramCount, stmtCount, threshold, targetTokens);
console.log('Similarity:', result.similarity); // 0.0-1.0
verification/unobfuscatableTests.ts)7 cas de test issus des preuves d'impossibilité du Paper 1, qui tentent d'extraire des secrets du code obfusqué :
import { runAllTests, printSummary } from './verification/unobfuscatableTests';
console.log(printSummary(runAllTests(10000)));
Tests : fonction point (mot de passe), nombres magiques, chaînes canari, clés intégrées, URL, motifs d'expressions régulières, signatures de flux de contrôle.
L'option --target-tokens contrôle la taille de sortie via un budget de gonflement qui module l'injection de code mort (principal levier de volume). Transformations conditionnées par le budget :
| Ratio du budget | Transformations activées |
|---|---|
| > 3 | Anti-débogage, déclencheurs, CFF, prédicats opaques, fusion à virgule |
| > 5 | Fonctions proxy, encodage des clés de propriétés, injection de bruit, vérification d'intégrité |
| > 8 | Épuisement de la fenêtre de contexte |
| > 10 | Encodage des variables globales |
Le multiplicateur de code mort évolue de 1x (ratio 30) à 150x (ratio 1500+), contrôlant le nombre et la taille des cas de switch morts et des branches de prédicats opaques.
src/
index.ts CLI entry point
obfuscator.ts Main pipeline orchestrator (20 transforms)
options.ts Budget system and options
types.ts AST type definitions
random.ts Random Unicode name generation (6-16 chars, 16 script ranges)
ast.ts AST node factory functions
keywords.ts Dynamic keyword discovery (globalThis + window package)
globals.ts Global state management (null-prototype maps)
substitute.ts Identifier substitution utilities
declarations.d.ts Module type declarations
passes/
firstPass.ts Identifier cataloging
secondPass.ts Identifier substitution + string encoding
thirdPass.ts Dummy parameter injection
transforms/
antiDebug.ts eval("debugger") traps + setInterval loops
tripwires.ts Punctured program secret-input checks [Paper 4]
noiseInjection.ts LPN-inspired arithmetic noise [Paper 7]
controlFlowFlattening.ts while/switch + modular arithmetic dispatch [Paper 3]
opaquePredicates.ts 15 always-true/false math predicates
proxyFunctions.ts Call graph flattening dispatchers
contextExhaustion.ts Ternary/void noise for LLM context filling
commaExpressions.ts Statement merging via comma operator
globalVariableEncoding.ts eval+replace for globals
propertyKeyEncoding.ts Computed property access with per-scope registries
numberEncoding.ts 11 bitwise/arithmetic encoding strategies
selfIntegrity.ts Anti-tamper runtime checks [Paper 10]
stringArrayExtraction.ts Chained XOR + sparse position errors [Papers 2, 9]
deadCodeInjection.ts Template + mutation-based dead code [Paper 3]
verification/
nullCircuitTest.ts Dead code quality scoring [Paper 10]
unobfuscatableTests.ts Secret extraction test cases [Paper 1]
__tests__/ 300+ unit tests across 21 suites
tools/
obfuscate-package.ts Webpack-based npm package compatibility testing
tests/
input*.js Original test input files
# Run all tests
npm test
# Run a specific test suite
npx jest controlFlowFlattening
npx jest tripwires
npx jest noiseInjection
# Test against npm packages (clones repos, webpack-bundles, obfuscates, runs tests)
npm run obfuscate-package # All 10 packages
npm run obfuscate-package -- minimist semver # Specific packages
L'outil tools/obfuscate-package.ts teste l'obfuscateur sur de véritables paquets npm :
| Chemin | Contenu |
|---|---|
dist/obfuscated/<package>/bundle.js | Le bundle webpack obfusqué pour chaque paquet |
dist/obfuscated/report.json | Rapport JSON complet avec tailles des bundles, statut d'obfuscation, sortie des tests |
MIT - Copyright 2026 Nicholas Starke
mangle: falsecompress: false