├── .gitignore ├── LICENSE.txt ├── README.md ├── demopage └── index.html ├── dist ├── lib.js └── main.js ├── index.html ├── lib.ts ├── main.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | test.html 3 | test.js 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # js-Z 2 | Javascript obfuscator, using the power of zalgo 3 | 4 | 5 | Try it here: https://kimbatt.github.io/js-Z 6 | 7 | With this tool, you can make your javascript code pretty unreadable. 8 | See demo page here: https://kimbatt.github.io/js-Z/demopage 9 | 10 | Disclaimer: this tool is not really a powerful obfuscator; with the right tools, the original code can be easily recovered (it works pretty well against online deobfuscators though). 11 | So if you really want to hide your code, you should run it through a *real* obfuscator first (like [this one](https://github.com/javascript-obfuscator/javascript-obfuscator)), then put its output in here. 12 | 13 | ### Using as a library 14 | Add [lib.ts](lib.ts) to your project (or [dist/lib.js](dist/lib.js) if you are using javascript), then call the `Obfuscate` function. 15 | See comments at the `Obfuscate` function and the `ObfuscationOptions` interface in [lib.ts](lib.ts) for more information. 16 | 17 | ### License 18 | [WTFPL](LICENSE.txt) 19 | -------------------------------------------------------------------------------- /demopage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /dist/lib.js: -------------------------------------------------------------------------------- 1 | const errorMessages = { 2 | [0 /* CannotGetVariableName */]: "Cannot get a variable name, try increasing the variable name length", 3 | [1 /* CannotGetVariableNameCustom */]: "Cannot get a variable name, try adding more custom characters or increase the variable name length", 4 | [2 /* CharsetIsIncompatibleES5 */]: "The selected charset is not supported for es5 target", 5 | [3 /* HasInvalidCustomCharacters */]: "Some of the custom characters cannot be used in variable names" 6 | }; 7 | export class ObfuscateError { 8 | type; 9 | message; 10 | invalidCharacters; // Not null if type is `HasInvalidCustomCharacters` 11 | constructor(type, invalidCharacters) { 12 | this.type = type; 13 | this.message = errorMessages[type]; 14 | this.invalidCharacters = invalidCharacters ?? null; 15 | } 16 | } 17 | // Simple random number generator with seed 18 | function Mulberry32(seed) { 19 | return () => { 20 | let t = seed += 0x6D2B79F5; 21 | t = Math.imul(t ^ t >>> 15, t | 1); 22 | t ^= t + Math.imul(t ^ t >>> 7, t | 61); 23 | return ((t ^ t >>> 14) >>> 0) / 4294967296; 24 | }; 25 | } 26 | let Random = Math.random; 27 | function Shuffle(array) { 28 | let current = array.length; 29 | let temp; 30 | let rand; 31 | while (current !== 0) { 32 | rand = Math.floor(Random() * current); 33 | --current; 34 | temp = array[current]; 35 | array[current] = array[rand]; 36 | array[rand] = temp; 37 | } 38 | } 39 | function Urand(min, max) { 40 | if (max === undefined) { 41 | return Math.floor(Random() * min); 42 | } 43 | return Math.floor(Random() * max - min) + min; 44 | } 45 | function RandomElementsOfStringArray(array, count) { 46 | let ret = ""; 47 | for (let i = 0; i < count; ++i) { 48 | ret += array[Urand(array.length)]; 49 | } 50 | return ret; 51 | } 52 | function RandomElementOf(array) { 53 | return array[Urand(array.length)]; 54 | } 55 | function IsValidName(name) { 56 | try { 57 | eval("var " + name); 58 | return true; 59 | } 60 | catch (e) { 61 | return false; 62 | } 63 | } 64 | const zalgoCharsExtended = [ 65 | "\u08f1", "\u0363", "\u0364", "\u0365", "\u0366", "\u0367", "\u0368", "\u0369", 66 | "\u036a", "\u036b", "\u036c", "\u036d", "\u036e", "\u036f", "\u0483", "\u0484", 67 | "\u0485", "\u0486", "\u0593", "\u0594", "\u0595", "\u0597", "\u0598", "\u059c", 68 | "\u059e", "\u059f", "\u05a0", "\u05a1", "\u05a8", "\u05a9", "\u05ab", "\u05ac", 69 | "\u05af", "\u05b5", "\u05c4", "\u0610", "\u0611", "\u0612", "\u0613", "\u0614", 70 | "\u0615", "\u0616", "\u0617", "\u0618", "\u0619", "\u061a", "\u0656", "\u0657", 71 | "\u0658", "\u065a", "\u065b", "\u065d", "\u065e", "\u065f", "\u0674", "\u06d6", 72 | "\u06d7", "\u06d8", "\u06d9", "\u06da", "\u06db", "\u06dc", "\u06df", "\u06e0", 73 | "\u06e1", "\u06e2", "\u06e3", "\u06e4", "\u06e7", "\u06e8", "\u06ea", "\u06eb", 74 | "\u06ec", "\u06ed", "\u08e4", "\u08e5", "\u08e6", "\u08e7", "\u08e8", 75 | "\u08e9", "\u08f0", "\u08f2", "\u08f3", "\u08f4", "\u08f5", "\u08f6", "\u08f7", 76 | "\u08f8", "\u08f9", "\u08fa", "\u08fb", "\u08fc", "\u08fd", "\u08fe", "\u0e31", 77 | "\u0e34", "\u0e35", "\u0e36", "\u0e37", "\u0e38", "\u0e39", "\u0e3a", "\u0e47", 78 | "\u0e48", "\u0e49", "\u0e4a", "\u0e4b", "\u0e4c", "\u0e4d", "\u0e4e", "\u0ec9", 79 | "\u0f19", "\u0f35", "\u0f37", "\u0f72", "\u0f7a", "\u0f7b", "\u0f7c", "\u0f7d", 80 | "\u0f80", "\u0f84", "\u1dc0", "\u1dc1", "\u1dc2", "\u1dc3", "\u1dc4", "\u1dc5", 81 | "\u1dc6", "\u1dc7", "\u1dc8", "\u1dc9", "\u1dca", "\u1dfe", "\u1dff", "\ufb1e", 82 | "\ufc5e", "\ufc5f", "\ufc60", "\ufc61", "\ufc62", "\ufc63" 83 | ]; 84 | // Stolen from http://eeemo.net/ 85 | const zalgo_up = [ 86 | '\u030d', '\u030e', '\u0304', '\u0305', 87 | '\u033f', '\u0311', '\u0306', '\u0310', 88 | '\u0352', '\u0357', '\u0351', '\u0307', 89 | '\u0308', '\u030a', '\u0342', '\u0343', 90 | '\u0344', '\u034a', '\u034b', '\u034c', 91 | '\u0303', '\u0302', '\u030c', '\u0350', 92 | '\u0300', '\u0301', '\u030b', '\u030f', 93 | '\u0312', '\u0313', '\u0314', '\u033d', 94 | '\u0309', '\u0363', '\u0364', '\u0365', 95 | '\u0366', '\u0367', '\u0368', '\u0369', 96 | '\u036a', '\u036b', '\u036c', '\u036d', 97 | '\u036e', '\u036f', '\u033e', '\u035b', 98 | '\u0346', '\u031a' 99 | ]; 100 | const zalgo_down = [ 101 | '\u0316', '\u0317', '\u0318', '\u0319', 102 | '\u031c', '\u031d', '\u031e', '\u031f', 103 | '\u0320', '\u0324', '\u0325', '\u0326', 104 | '\u0329', '\u032a', '\u032b', '\u032c', 105 | '\u032d', '\u032e', '\u032f', '\u0330', 106 | '\u0331', '\u0332', '\u0333', '\u0339', 107 | '\u033a', '\u033b', '\u033c', '\u0345', 108 | '\u0347', '\u0348', '\u0349', '\u034d', 109 | '\u034e', '\u0353', '\u0354', '\u0355', 110 | '\u0356', '\u0359', '\u035a', '\u0323' 111 | ]; 112 | const zalgo_mid = [ 113 | '\u0315', '\u031b', '\u0340', '\u0341', 114 | '\u0358', '\u0321', '\u0322', '\u0327', 115 | '\u0328', '\u0334', '\u0335', '\u0336', 116 | '\u034f', '\u035c', '\u035d', '\u035e', 117 | '\u035f', '\u0360', '\u0362', '\u0338', 118 | '\u0337', '\u0361' 119 | ]; 120 | const standaloneChars = [ 121 | "\u13B0", "\u13B1", "\u2C0D", "\u2CB2", 122 | "\u2CBA", "\uA722", "\uA724", "\uA78B", 123 | "\u02B9", "\u02BA", "\u02BB", "\u02BC", 124 | "\u02BD", "\u02BE", "\u02BF", "\u02C6", 125 | "\u02C7", "\u02C8", "\u02C9", "\u02CA", 126 | "\u02CB", "\u02CC", "\u02CE", "\u02CF", 127 | "\u02D0", "\u02D1", "\u02EC", "\u0374", 128 | "\u037A", "\u0559", "\u0640", "\u07F4", 129 | "\u07F5", "\u07FA", "\u0971", "\u1843", 130 | "\u1C78", "\u1C79", "\u1C7A", "\u1C7B", 131 | "\u1C7C", "\u1D54", "\u1D55", "\u2D6F", 132 | "\u3035", "\u3033", "\u309D", 133 | "\u30FC", "\u30FD", "\u30FE", "\uA4F8", 134 | "\uA4F9", "\uA4FA", "\uA4FB", "\uA4FC", 135 | "\uA4FD", "\uA67F", "\uA788", "\uFF70", 136 | "\uFF9E", "\uFF9F", "\uA9E6", 137 | "\u05D9", "\u05D5", "\u0621", "\u0629", 138 | "\u0647", "\u06C0", "\u06C1", "\u06C2", 139 | "\u06C3", "\u06D5", "\u0715", "\u0716", 140 | "\u0718", "\u0719", "\u071A", "\u071D", 141 | "\u0722", "\u072A", "\u072F", "\u074D", 142 | "\u075A", "\u0765", "\u0766", "\u0780", 143 | "\u0789", "\u0799", "\u079A", "\u07D1", 144 | "\u07EA", "\u0B83", "\u0B9F", "\u0CF2", 145 | "\u0E14", "\u0E15", "\u0E16", "\u0E23", 146 | "\u0E27", "\u0E32", "\u0E33", "\u0E40", 147 | "\u0E45", "\u0EB0", "\u0EB2", 148 | "\u0EC0", "\u0F44", "\u0F4B", "\u0F50", 149 | "\u0F54", "\u0F55", "\u0F56", "\u0F66", 150 | "\u113C", "\u113E", "\u1173", "\u119E", 151 | "\u11A2", "\u141D", "\u141F", "\u1420", 152 | "\u1425", "\u1426", "\u1427", "\u1428", 153 | "\u1429", "\u1449", "\u144A", "\u14BB", 154 | "\u14BC", "\u14BD", "\u14BE", "\u14D0", 155 | "\u1508", "\u1540", "\u153E", 156 | ]; 157 | const startingChars = [ 158 | "\u02b9", "\u02ba", "\u02bb", "\u02bc", "\u02bd", "\u02be", "\u02bf", "\u02c6", 159 | "\u02c7", "\u02c8", "\u02cc", "\u02d1", "\u0374", "\u037a", "\u0559", "\u1843", 160 | "\u1c78", "\u1c79", "\u1c7a", "\u1c7c", "\u1d54", "\u1d55", "\u1da5", 161 | "\u309d", "\u30fd", "\ua4f8", "\ua4f9", "\ua4fa", "\ua4fb", "\ua4fc", "\ua4fd", 162 | "\ua67f", "\ua717", "\ua718", "\ua719", "\ua71a", "\ua788", "\uff9e", "\uff9f", 163 | "\ua9e6", "\u05d9", "\u0621", "\u0627", "\u0674", "\u06c0", "\u06c1", "\u06c2", 164 | "\u06c3", "\u06d5", "\u0715", "\u0716", "\u0719", "\u071d", "\u072a", "\u072f", 165 | "\u0780", "\u0787", "\u0789", "\u0799", "\u079a", "\u07a2", "\u07a3", "\u07d1", 166 | "\u07e8", "\u0eb2", "\u03b3", "\u0f44", "\u0f4b", "\u0f56", "\u0f8b", "\u141d", 167 | "\u141e", "\u141f", "\u1420", "\u1425", "\u1426", "\u1427", "\u1449", "\u14a1", 168 | "\u14a2", "\u14bb", "\u14bc", "\u14bd", "\u14d0", "\u14d2", "\u14ea", "\u1505", 169 | "\u1507", "\u1508", "\u1509", "\u150a", "\u153e", "\u1550", "\u155d", "\u156a", 170 | "\u1595", "\u159f", "\u15a6", "\u15ae", "\u1690", "\u1691", "\u1692", "\u1693", 171 | "\u1694", "\u16b2", "\u16cd", "\u16e7", "\u1822", "\u1823", "\u1824", "\u1825", 172 | "\u1826", "\u1827", "\u1829", "\u182b", "\u1830", "\u1831", "\u1832", "\u1833", 173 | "\u1834", "\u1835", "\u1836", "\u1837", "\u1838", "\u1839", "\u183a", "\u183b", 174 | "\u184d", "\u1855", "\u185f", "\u186a", "\u188f", "\u189c", "\u18a4", "\u18a5", 175 | "\u18a6", "\u18d9", "\u18da", "\u18de", "\u18df", "\u2d30", "\u2d3e", "\u2d42", 176 | "\u2d46", "\u2d48", "\u2d53", "\u2d57", "\u318d", "\u31b4", "\u31b5", "\u31b6" 177 | ]; 178 | const baseChars = [ 179 | "\u02b9", "\u02ba", "\u02bb", "\u02bc", "\u02bd", "\u02be", "\u02bf", "\u02c6", 180 | "\u02c7", "\u02c8", "\u02cc", "\u02d1", "\u0374", "\u037a", "\u0559", "\u1d54", 181 | "\u1d55", "\u1da5", "\ua4f8", "\ua4f9", "\ua4fa", "\ua4fb", "\ua4fc", "\ua4fd", 182 | "\ua67f", "\ua717", "\ua718", "\ua719", "\ua71a", "\ua788", "\u05d9", "\u0621", 183 | "\u0627", "\u0674", "\u06c0", "\u06c1", "\u06c2", "\u06c3", "\u06d5", "\u03b3" 184 | ]; 185 | const linesLetters = [ 186 | "\u13B1", "\u13DD", 187 | "\u13DE", "\u13DF", 188 | "\u2C84", "\u2CA4", 189 | "\u2CB6", "\u2CBA", 190 | "\u2CD0", "\u2CDE", 191 | "\uA78B", "\u{10411}", 192 | "\u{10415}", "\u{1041B}", 193 | "\u{10423}", "\u{1D5A2}", 194 | "\u{1D5A8}", "\u{1D5A9}", 195 | "\u{1D5AB}", "\u{1D5D6}", 196 | "\u{1D5DC}", "\u{1D5DD}", 197 | "\u{1D5DF}", "\u{1D758}", 198 | "\u{1D75E}", "\u{1D763}", 199 | "\u2C85", "\u2C93", 200 | "\u2CA5", "\u2CB7", 201 | "\u2CBB", "\u2CDF", 202 | "\uA78C", "\u{10439}", 203 | "\u{1043D}", "\u{10443}", 204 | "\u{1044B}", "\u{1D5C5}", 205 | "\u{1D5F9}", "\uAB81", 206 | "\uABAE", "\uABAF", 207 | "\u02B9", "\u02BB", 208 | "\u02BC", "\u02BD", 209 | "\u02C8", "\u02C9", 210 | "\u02CA", "\u02CB", 211 | "\u02CC", "\u02CD", 212 | "\u02CE", "\u02CF", 213 | "\u0374", "\u0559", 214 | "\u07F4", "\u07F5", 215 | "\u1C7B", "\u1C7C", 216 | "\uA4F9", "\uA60C", 217 | "\uFF70", "\u01C0", 218 | "\u01C1", "\u0B9F", 219 | "\u1100", "\u1102", 220 | "\u115F", "\u1160", 221 | "\u1173", "\u1175", 222 | "\u119E", "\u11A1", 223 | "\u11A8", "\u11AB", 224 | "\u11FA", "\u1427", 225 | "\u1428", "\u1429", 226 | "\u1433", "\u1438", 227 | "\u1440", "\u1441", 228 | "\u1444", "\u1445", 229 | "\u1449", "\u144A", 230 | "\u14A3", "\u14A5", 231 | "\u14A7", "\u14AA", 232 | "\u14AC", "\u14AF", 233 | "\u14B2", "\u14B7", 234 | "\u1540", "\u167F", 235 | "\u16C1", "\u16CC", 236 | "\u16E7", "\u18B4", 237 | "\u18B5", "\u18B6", 238 | "\u18DE", "\u18DF", 239 | "\u1963", "\u2D4E", 240 | "\u2D4F", "\u30CB", 241 | "\u30FF", "\u3127", 242 | "\u3131", "\u3134", 243 | "\u3137", "\u3161", 244 | "\u3163", "\u318D", 245 | "\u318E", 246 | //"\uFF9E", "\uFF9F", 247 | ]; 248 | const linesLetters_compatibility = [ 249 | "\u13b1", "\u13dd", "\u13de", "\u13df", 250 | "\u2c84", "\u2ca4", "\u2cb6", "\u2cba", 251 | "\u2cd0", "\u2cde", "\ua78b", "\u2c85", 252 | "\u2c93", "\u2ca5", "\u2cb7", "\u2cbb", 253 | "\u2cdf", "\ua78c", "\u02bb", "\u02bc", 254 | "\u02bd", "\u0559", "\u07f4", "\u07f5", 255 | "\u1c7b", "\u1c7c", "\ua60c", "\uff70", 256 | "\u01c0", "\u01c1", "\u0b9f", "\u1100", 257 | "\u1102", "\u115f", "\u1160", "\u1173", 258 | "\u1175", "\u119e", "\u11a1", "\u11a8", 259 | "\u11ab", "\u1427", "\u1428", "\u1429", 260 | "\u1433", "\u1438", "\u1440", "\u1441", 261 | "\u1444", "\u1445", "\u1449", "\u144a", 262 | "\u14a3", "\u14a5", "\u14a7", "\u14aa", 263 | "\u14ac", "\u14af", "\u14b2", "\u14b7", 264 | "\u1540", "\u16c1", "\u16cc", "\u16e7", 265 | "\u1963", "\u2d4e", "\u2d4f", "\u30cb", 266 | "\u30ff", "\u3127", "\u3131", "\u3134", 267 | "\u3137", "\u3161", "\u3163", "\u318d", 268 | "\u318e" 269 | ]; 270 | const iLetters = ["\u0049", "\u0399", "\u0406"]; 271 | function Unreachable(_) { 272 | throw Error("Unreachable"); 273 | } 274 | export function CheckCustomCharacters(characters) { 275 | // Unique characters 276 | const startingChars = new Set(); 277 | const nonStartingChars = new Set(); 278 | const invalidChars = new Set(); 279 | // Same characters as above, but characters can appear multiple times 280 | const allNonStartingChars = []; 281 | const allStartingChars = []; 282 | // These can cause `"var a" + char` to be valid code, so these are skipped early 283 | const badCharacters = ["\n", "\r", "\t", " ", ";"]; 284 | for (const char of characters) { 285 | if (char.length === 0) { 286 | // Skip empty 287 | continue; 288 | } 289 | if (badCharacters.includes(char)) { 290 | // Skip bad character 291 | invalidChars.add(char); 292 | continue; 293 | } 294 | if (char.length !== 1) { 295 | // Longer "character", must not contain any of the bad characters 296 | let valid = true; 297 | for (const ch of char) { 298 | if (badCharacters.includes(ch)) { 299 | valid = false; 300 | break; 301 | } 302 | } 303 | if (!valid) { 304 | invalidChars.add(char); 305 | continue; 306 | } 307 | } 308 | if (invalidChars.has(char)) { 309 | // Already checked, invalid 310 | continue; 311 | } 312 | if (nonStartingChars.has(char)) { 313 | // Already checked, valid 314 | // Add again to increase chance 315 | allNonStartingChars.push(char); 316 | continue; 317 | } 318 | if (startingChars.has(char)) { 319 | // Same as before, but for starting characters 320 | allStartingChars.push(char); 321 | continue; 322 | } 323 | try { 324 | // Check if the current character is valid as a starting character in a variable name 325 | eval("var " + char); 326 | // If no exception is thrown, then it's valid 327 | startingChars.add(char); 328 | allStartingChars.push(char); 329 | } 330 | catch { } 331 | try { 332 | // Check again, but as a non-starting character 333 | eval("var a" + char); 334 | nonStartingChars.add(char); 335 | allNonStartingChars.push(char); 336 | } 337 | catch { 338 | // The current character cannot be used 339 | invalidChars.add(char); 340 | } 341 | } 342 | return { 343 | startingChars: allStartingChars, 344 | nonStartingChars: allNonStartingChars, 345 | invalidChars, 346 | }; 347 | } 348 | let debugVarIndex = 0; 349 | let debuggingVarNames = false; 350 | function GetRandomVariableName(length, allowLonger, options, customCharacterData) { 351 | if (debuggingVarNames) { 352 | return "a_" + (debugVarIndex++); 353 | } 354 | const { charset } = options; 355 | let name = ""; 356 | switch (charset.type) { 357 | case "zalgo": 358 | { 359 | name = RandomElementOf(startingChars); 360 | const zalgoLevel = charset.zalgoLevel ?? 3; 361 | while (name.length < length) { 362 | if (zalgoLevel === 0) { 363 | name += RandomElementOf(standaloneChars); 364 | } 365 | else { 366 | if (Random() < 0.9) { 367 | // Add a random number of zalgo characters with a starting character 368 | name += RandomElementOf(baseChars); 369 | const min = zalgoLevel; 370 | const max = zalgoLevel * 1.5; 371 | const diff = max - min; 372 | name += RandomElementsOfStringArray(zalgo_up, Math.floor(Random() * diff + min)); 373 | name += RandomElementsOfStringArray(zalgo_mid, Math.floor(Random() * diff + min)); 374 | name += RandomElementsOfStringArray(zalgo_down, Math.floor(Random() * diff + min)); 375 | continue; 376 | } 377 | else { 378 | // Add a random extended zalgo character with a starting character 379 | // The only character that actually works and is not a letter is _ 380 | name += "_"; 381 | const count = Urand(zalgoLevel, zalgoLevel * 1.5); 382 | const ch = RandomElementOf(zalgoCharsExtended); 383 | for (let j = 0; j < count; ++j) { 384 | name += ch; 385 | } 386 | continue; 387 | } 388 | } 389 | } 390 | break; 391 | } 392 | case "lines": 393 | { 394 | name = RandomElementsOfStringArray(options.target === "es5-" ? linesLetters_compatibility : linesLetters, length); 395 | break; 396 | } 397 | case "invisible": 398 | { 399 | name = "\u02cb"; 400 | for (let i = 0; i < length; ++i) { 401 | name += String.fromCharCode(0xfe00 + (Random() * 16) | 0); 402 | } 403 | break; 404 | } 405 | case "iiii": 406 | { 407 | name = RandomElementsOfStringArray(iLetters, length); 408 | break; 409 | } 410 | case "custom": 411 | { 412 | name = 413 | RandomElementsOfStringArray(customCharacterData.startingChars, 1) + 414 | RandomElementsOfStringArray(customCharacterData.nonStartingChars, length - 1); 415 | break; 416 | } 417 | default: 418 | Unreachable(charset); 419 | } 420 | if (allowLonger) { 421 | return name; 422 | } 423 | // Trim name to the given length 424 | const chars = [...name].slice(0, length); 425 | return chars.join(""); 426 | } 427 | function GetNewVariableName(usedVariableNames, options, customCharacterData) { 428 | const { variableNameLength, charset } = options; 429 | const allowLongerName = charset.type === "zalgo"; 430 | let len; 431 | if (allowLongerName) { 432 | const min = variableNameLength; 433 | const max = variableNameLength * 1.5; 434 | len = Math.floor(Random() * (max - min) + min); 435 | } 436 | else { 437 | len = variableNameLength; 438 | } 439 | const maxAttempts = 100; 440 | for (let i = 0; i < maxAttempts; ++i) { 441 | const name = GetRandomVariableName(len, allowLongerName, options, customCharacterData); 442 | if (!IsValidName(name)) { 443 | // Should not happen, was already checked before 444 | continue; 445 | } 446 | if (!usedVariableNames.has(name)) { 447 | // Found a new variable name which has not been used yet 448 | usedVariableNames.add(name); 449 | return name; 450 | } 451 | } 452 | // Could not get new variable name 453 | if (charset.type === "custom") { 454 | throw new ObfuscateError(1 /* CannotGetVariableNameCustom */); 455 | } 456 | else { 457 | throw new ObfuscateError(0 /* CannotGetVariableName */); 458 | } 459 | } 460 | function GetCharCodes(str) { 461 | // Note: this will split characters with char code >0xffff into two characters 462 | // But those will be re-combined after string concatenation, so it will work fine 463 | // By not using code points, we only need to go to 32768 for the highest number bit, instead of 524288, which saves some space 464 | // return str.split("").map(ch => ch.charCodeAt(0)); 465 | const codes = new Array(str.length); 466 | for (let i = 0; i < str.length; ++i) { 467 | codes[i] = str.charCodeAt(i); 468 | } 469 | return codes; 470 | } 471 | function GetIntFromBits(number, i0, i1, i2, i4, i8, i16, i32, i64, i128, i256, i512, i1024, i2048, i4096, i8192, i16384, i32768) { 472 | // Construct a number from bits, using the given variable names 473 | // e.g. the number 20 can be constructed from 4 + 16 474 | if (number === 0) { 475 | return i0; 476 | } 477 | const ret = []; 478 | const numberNames = [i1, i2, i4, i8, i16, i32, i64, i128, i256, i512, i1024, i2048, i4096, i8192, i16384, i32768]; 479 | for (const num of numberNames) { 480 | if (number & 1) { 481 | ret.push(num); 482 | } 483 | number >>= 1; 484 | } 485 | Shuffle(ret); 486 | let retStr = ret[0]; // Should always have at least one element 487 | for (let i = 1; i < ret.length; ++i) { 488 | // Use + or | at random; since the numbers are powers of 2, both have the same effect 489 | retStr += Random() < 0.5 ? "+" : "|"; 490 | retStr += ret[i]; 491 | } 492 | return retStr; 493 | } 494 | function ValidateRngSeed(seed) { 495 | if (Number.isNaN(seed) || !Number.isFinite(seed)) { 496 | return 0; 497 | } 498 | else if (seed < 0) { 499 | return 0; 500 | } 501 | else if (seed > 0xffffffff) { 502 | return 0xffffffff; 503 | } 504 | return Math.floor(seed); 505 | } 506 | // Returns the obfuscated version of the input code 507 | // Throws `ObfuscateError` if the code could not be obfuscated for some reason 508 | // See comments for `ObfuscationOptions` for more information about the options (everything is optional) 509 | export function Obfuscate(inputCode, options) { 510 | if (inputCode === "") { 511 | return ""; 512 | } 513 | const optionsValidated = { 514 | charset: options?.charset ?? { type: "zalgo" }, 515 | variableNameLength: Math.max(options?.variableNameLength ?? 2, 2), 516 | target: options?.target ?? "es6+", 517 | deobfuscationProtection: options?.deobfuscationProtection ?? null 518 | }; 519 | const rngSeed = options?.rngSeed ?? null; 520 | Random = (rngSeed === null) ? Math.random : Mulberry32(ValidateRngSeed(rngSeed)); 521 | const { charset, variableNameLength, target, deobfuscationProtection } = optionsValidated; 522 | if (target === "es5-") { 523 | if (charset.type === "zalgo" || charset.type === "invisible") { 524 | throw new ObfuscateError(2 /* CharsetIsIncompatibleES5 */); 525 | } 526 | } 527 | // Check custom characters, or just use an empty array if the charset is not custom 528 | const customCharacterData = CheckCustomCharacters(charset.type === "custom" ? charset.characters : []); 529 | if (customCharacterData.invalidChars.size !== 0) { 530 | throw new ObfuscateError(3 /* HasInvalidCustomCharacters */, customCharacterData.invalidChars); 531 | } 532 | const usedVariableNames = new Set(); 533 | function GetVariableName() { 534 | return GetNewVariableName(usedVariableNames, optionsValidated, customCharacterData); 535 | } 536 | const resultSegments = []; 537 | function AddToResult(str) { 538 | resultSegments.push(str); 539 | } 540 | AddToResult("(" + (target === "es5-" ? "function()" : "()=>") + "{var "); 541 | const v_number_0 = GetVariableName(); 542 | const v_number_1 = GetVariableName(); 543 | const v_number_2 = GetVariableName(); 544 | const v_number_4 = GetVariableName(); 545 | const v_number_8 = GetVariableName(); 546 | const v_number_16 = GetVariableName(); 547 | const v_number_32 = GetVariableName(); 548 | const v_number_64 = GetVariableName(); 549 | const v_number_128 = GetVariableName(); 550 | const v_number_256 = GetVariableName(); 551 | const v_number_512 = GetVariableName(); 552 | const v_number_1024 = GetVariableName(); 553 | const v_number_2048 = GetVariableName(); 554 | const v_number_4096 = GetVariableName(); 555 | const v_number_8192 = GetVariableName(); 556 | const v_number_16384 = GetVariableName(); 557 | const v_number_32768 = GetVariableName(); 558 | // Generate variables for each letter in the input code 559 | const letters = new Map(); 560 | function GetNumber(num) { 561 | return GetIntFromBits(num, v_number_0, v_number_1, v_number_2, v_number_4, v_number_8, v_number_16, v_number_32, v_number_64, v_number_128, v_number_256, v_number_512, v_number_1024, v_number_2048, v_number_4096, v_number_8192, v_number_16384, v_number_32768); 562 | } 563 | const inputCharCodes = GetCharCodes(inputCode); 564 | // Collect unique characters from the input text 565 | for (const ch of inputCharCodes) { 566 | if (letters.has(ch)) { 567 | continue; 568 | } 569 | const variableName = GetVariableName(); 570 | letters.set(ch, { variableName, assignmentExpr: variableName + "=" + GetNumber(ch) }); 571 | } 572 | const allowLongerName = charset.type === "zalgo"; 573 | function GetRandomStr(allowLonger) { 574 | return GetRandomVariableName(variableNameLength, allowLonger, optionsValidated, customCharacterData); 575 | } 576 | AddToResult(v_number_0 + "='" + GetRandomStr(allowLongerName) + "'&'" + GetRandomStr(allowLongerName) + "',"); 577 | AddToResult(v_number_1 + "=-~'" + GetRandomStr(allowLongerName) + "',"); 578 | AddToResult(v_number_2 + "=" + v_number_1 + "-~'" + GetRandomStr(false) + "',"); 579 | AddToResult(v_number_4 + "=" + v_number_2 + "+" + v_number_2 + ","); 580 | AddToResult(v_number_8 + "=" + v_number_4 + "*" + v_number_2 + ","); 581 | AddToResult(v_number_16 + "=" + v_number_2 + "*" + v_number_4 + "*" + v_number_2 + ","); 582 | AddToResult(v_number_32 + "=" + v_number_8 + "*" + v_number_4 + ","); 583 | AddToResult(v_number_64 + "=" + v_number_4 + "*" + v_number_16 + ","); 584 | AddToResult(v_number_128 + "=" + v_number_8 + "*" + v_number_2 + "*" + v_number_8 + ","); 585 | AddToResult(v_number_256 + "=" + v_number_64 + "*" + v_number_4 + ","); 586 | AddToResult(v_number_512 + "=" + v_number_8 + "*" + v_number_64 + ","); 587 | AddToResult(v_number_1024 + "=" + v_number_4 + "*" + v_number_256 + ","); 588 | AddToResult(v_number_2048 + "=" + v_number_64 + "*" + v_number_32 + ","); 589 | AddToResult(v_number_4096 + "=" + v_number_256 + "*" + v_number_16 + ","); 590 | AddToResult(v_number_8192 + "=" + v_number_8 + "*" + v_number_1024 + ","); 591 | AddToResult(v_number_16384 + "=" + v_number_32 + "*" + v_number_512 + ","); 592 | AddToResult(v_number_32768 + "=" + v_number_4 + "*" + v_number_8192 + ","); 593 | // "acCdefghilmnoprsStuv. " <- we need these characters 594 | // "false" = !1+[] 595 | const v_string_false = GetVariableName(); 596 | AddToResult(v_string_false + "=!" + v_number_1 + "+[],"); 597 | // f = "false"[0] 598 | const v_char_f = GetVariableName(); 599 | AddToResult(v_char_f + "=" + v_string_false + "[" + v_number_0 + "],"); 600 | // "undefined" = "f"[1]+[] 601 | const v_string_undefined = GetVariableName(); 602 | AddToResult(v_string_undefined + "=" + v_char_f + "[" + v_number_1 + "]+[],"); 603 | // a = "false"[1] 604 | const v_char_a = GetVariableName(); 605 | AddToResult(v_char_a + "=" + v_string_false + "[" + v_number_1 + "],"); 606 | // l = "false"[2] 607 | const v_char_l = GetVariableName(); 608 | AddToResult(v_char_l + "=" + v_string_false + "[" + v_number_2 + "],"); 609 | // s = "false"[3] 610 | const v_char_s = GetVariableName(); 611 | AddToResult(v_char_s + "=" + v_string_false + "[" + v_number_2 + "+" + v_number_1 + "],"); 612 | // e = "false"[4] 613 | const v_char_e = GetVariableName(); 614 | AddToResult(v_char_e + "=" + v_string_false + "[" + v_number_4 + "],"); 615 | // i = "undefined"[5] 616 | const v_char_i = GetVariableName(); 617 | AddToResult(v_char_i + "=" + v_string_undefined + "[" + v_number_4 + "+" + v_number_1 + "],"); 618 | // "[object Object]" 619 | const v_string_object_object = GetVariableName(); 620 | AddToResult(v_string_object_object + "=[]+{},"); 621 | // c = "[object Object]"[5] 622 | const v_char_c = GetVariableName(); 623 | AddToResult(v_char_c + "=" + v_string_object_object + "[" + v_number_4 + "+" + v_number_1 + "],"); 624 | // b = "[object Object]"[2] 625 | const v_char_b = GetVariableName(); 626 | AddToResult(v_char_b + "=" + v_string_object_object + "[" + v_number_2 + "],"); 627 | // u = "undefined"[0] 628 | const v_char_u = GetVariableName(); 629 | AddToResult(v_char_u + "=" + v_string_undefined + "[" + v_number_0 + "],"); 630 | // n = "undefined"[1] 631 | const v_char_n = GetVariableName(); 632 | AddToResult(v_char_n + "=" + v_string_undefined + "[" + v_number_1 + "],"); 633 | // t = (!0+[])[0] 634 | const v_char_t = GetVariableName(); 635 | AddToResult(v_char_t + "=(!" + v_number_0 + "+[])[" + v_number_0 + "],"); 636 | // o = "[object Object]"[1] 637 | const v_char_o = GetVariableName(); 638 | AddToResult(v_char_o + "=" + v_string_object_object + "[" + v_number_1 + "],"); 639 | // space = "[object Object]"[7] 640 | const v_char_space = GetVariableName(); 641 | AddToResult(v_char_space + "=" + v_string_object_object + "[" + v_number_8 + "-" + v_number_1 + "],"); 642 | // d = "undefined"[2] 643 | const v_char_d = GetVariableName(); 644 | AddToResult(v_char_d + "=" + v_string_undefined + "[" + v_number_2 + "],"); 645 | // r = (!0+[])[1] 646 | const v_char_r = GetVariableName(); 647 | AddToResult(v_char_r + "=(!" + v_number_0 + "+[])[" + v_number_1 + "],"); 648 | // "constructor" 649 | const v_string_constructor = GetVariableName(); 650 | AddToResult(v_string_constructor + "=" + 651 | [v_char_c, v_char_o, v_char_n, v_char_s, v_char_t, v_char_r, v_char_u, v_char_c, v_char_t, v_char_o, v_char_r,].join("+") 652 | + ","); 653 | // []["filter"] 654 | const v_function_filter = GetVariableName(); 655 | AddToResult(v_function_filter + "=[][" + [v_char_f, v_char_i, v_char_l, v_char_t, v_char_e, v_char_r].join("+") + "],"); 656 | // []["filter"]["constructor"] 657 | const v_function_function = GetVariableName(); 658 | AddToResult(v_function_function + "=" + v_function_filter + "[" + v_string_constructor + "],"); 659 | const v_char_g = GetVariableName(); 660 | const v_char_S = GetVariableName(); 661 | if (target === "nodejs") { 662 | // `btoa` is not always available on node.js, so we need to use a different approach 663 | // Here, we convert native functions to strings, which always works in node.js, 664 | // but is not consistent across browsers, because it's not part of the ECMAScript standard 665 | // ''["constructor"]+[] 666 | const v_string_StringConstructor = GetVariableName(); 667 | AddToResult(v_string_StringConstructor + "=''[" + v_string_constructor + "]+[],"); 668 | // S = (''["constructor"]+[])[1+8] 669 | AddToResult(v_char_S + "=" + v_string_StringConstructor + "[" + v_number_1 + "+" + v_number_8 + "],"); 670 | // g = (''["constructor"]+[])[16-2] 671 | AddToResult(v_char_g + "=" + v_string_StringConstructor + "[" + v_number_16 + "-" + v_number_2 + "],"); 672 | } 673 | else { 674 | // []["filter"]["constructor"]("return btoa")() 675 | const v_function_btoa = GetVariableName(); 676 | AddToResult(v_function_btoa + "=" + v_function_function + "(" + 677 | [v_char_r, v_char_e, v_char_t, v_char_u, v_char_r, v_char_n, v_char_space, v_char_b, v_char_t, v_char_o, v_char_a].join("+") 678 | + ")(),"); 679 | // S = btoa("a ")[1] 680 | AddToResult(v_char_S + "=" + v_function_btoa + "(" + v_char_a + "+" + v_char_space + ")[" + v_number_1 + "],"); 681 | // g = btoa("b")[1] 682 | AddToResult(v_char_g + "=" + v_function_btoa + "(" + v_char_b + ")[" + v_number_1 + "],"); 683 | } 684 | // "toString" 685 | const v_string_toString = GetVariableName(); 686 | AddToResult(v_string_toString + "=" + [v_char_t, v_char_o, v_char_S, v_char_t, v_char_r, v_char_i, v_char_n, v_char_g].join("+") + ","); 687 | // p = (16+8+1)["toString"](32) 688 | const v_char_p = GetVariableName(); 689 | AddToResult(v_char_p + "=(" + v_number_1 + "+" + v_number_8 + "+" + v_number_16 + ")[" + v_string_toString + "](" + v_number_32 + "),"); 690 | // []["constructor"]["constructor"]("return escape")() 691 | const v_function_escape = GetVariableName(); 692 | AddToResult(v_function_escape + "=[][" + v_string_constructor + "][" + v_string_constructor + "](" + 693 | [v_char_r, v_char_e, v_char_t, v_char_u, v_char_r, v_char_n, v_char_space, v_char_e, v_char_s, v_char_c, v_char_a, v_char_p, v_char_e].join("+") 694 | + ")(),"); 695 | // C = escape(''["big"]())[2] 696 | const v_char_C = GetVariableName(); 697 | AddToResult(v_char_C + "=" + v_function_escape + "(''[" + [v_char_b, v_char_i, v_char_g].join("+") + "]())[" + v_number_2 + "],"); 698 | // m = (16+4+2)["toString"](32) 699 | const v_char_m = GetVariableName(); 700 | AddToResult(v_char_m + "=(" + v_number_16 + "+" + v_number_4 + "+" + v_number_2 + ")[" + v_string_toString + "](" + v_number_32 + "),"); 701 | // h = (16+1)["toString"](32) 702 | const v_char_h = GetVariableName(); 703 | AddToResult(v_char_h + "=(" + v_number_16 + "+" + v_number_1 + ")[" + v_string_toString + "](" + v_number_32 + "),"); 704 | // h = (32-1)["toString"](32) 705 | const v_char_v = GetVariableName(); 706 | AddToResult(v_char_v + "=(" + v_number_32 + "-" + v_number_1 + ")[" + v_string_toString + "](" + v_number_32 + "),"); 707 | // ''["constructor"]["fromCharCode"] 708 | const v_function_stringFromCharCode = GetVariableName(); 709 | AddToResult(v_function_stringFromCharCode + "=''[" + v_string_constructor + "][" + 710 | [v_char_f, v_char_r, v_char_o, v_char_m, v_char_C, v_char_h, v_char_a, v_char_r, v_char_C, v_char_o, v_char_d, v_char_e].join("+") 711 | + "],"); 712 | // []["filter"]["constructor"]("return eval")() 713 | const v_function_eval = GetVariableName(); 714 | AddToResult(v_function_eval + "=" + v_function_function + "(" + 715 | [v_char_r, v_char_e, v_char_t, v_char_u, v_char_r, v_char_n, v_char_space, v_char_e, v_char_v, v_char_a, v_char_l].join("+") 716 | + ")(),"); 717 | let v_bool_deobfuscationOk = ""; 718 | if (deobfuscationProtection) { 719 | const dummyString = GetRandomStr(allowLongerName); 720 | const v_string_dummyString = GetVariableName(); 721 | AddToResult(v_string_dummyString + "='" + dummyString + "',"); 722 | const v_function_deobfuscationProtection = GetVariableName(); 723 | const functionStart = target === "es5-" ? "function()" : "()=>"; 724 | AddToResult(v_function_deobfuscationProtection + "=" + functionStart + "{'" + dummyString + "'},"); 725 | // How this works: 726 | // In javascript, you can convert functions to a string, and if you convert a user-defined function to string, 727 | // you'll get back the exact string representation of the function 728 | // Deobfuscators usually format the code to make it more readable, so we create a dummy function which will be formatted for sure 729 | // So we store the original string representation, and compare it with the new one 730 | // If the code was formatted, then those two won't match, and we can do stuff with that 731 | v_bool_deobfuscationOk = GetVariableName(); 732 | AddToResult(v_bool_deobfuscationOk + "=(" + v_function_deobfuscationProtection + "+[])[" 733 | + [v_char_s, v_char_u, v_char_b, v_char_s, v_char_t, v_char_r].join("+") 734 | + "](" + GetNumber(functionStart.length + 2) + "," + GetNumber(dummyString.length) + ")==" + v_string_dummyString + ","); 735 | if (deobfuscationProtection.type !== "skip") { 736 | const v_string_otherFunctionName = GetVariableName(); 737 | const v_string_param1_deobfuscationProtection = GetVariableName(); 738 | AddToResult(v_string_otherFunctionName + "=" + v_function_function + "('" + v_string_param1_deobfuscationProtection + "',"); 739 | function GetCharCodesOf(str) { 740 | const charCodes = GetCharCodes(str); 741 | return v_function_stringFromCharCode + "(" + charCodes.map(charCode => GetNumber(charCode)) 742 | .join(",") + ")"; 743 | } 744 | switch (deobfuscationProtection.type) { 745 | case "error": 746 | { 747 | AddToResult(GetCharCodesOf("!function \u0192(){" + v_string_param1_deobfuscationProtection + "=='" + dummyString + "'||\u0192()}()")); 748 | break; 749 | } 750 | case "loop": 751 | { 752 | AddToResult(GetCharCodesOf("if(" + v_string_param1_deobfuscationProtection + "!='" + dummyString + "')for(;;){}")); 753 | break; 754 | } 755 | case "custom": 756 | { 757 | AddToResult(GetCharCodesOf(v_string_param1_deobfuscationProtection + "=='" + dummyString 758 | + "'||!function(){" + deobfuscationProtection.codeToRun + "}()")); 759 | break; 760 | } 761 | default: 762 | Unreachable(deobfuscationProtection); 763 | } 764 | AddToResult(")((" + v_function_deobfuscationProtection + "+[])[" 765 | + [v_char_s, v_char_u, v_char_b, v_char_s, v_char_t, v_char_r].join("+") 766 | + "](" + GetNumber(functionStart.length + 2) + "," + GetNumber(dummyString.length) + ")),"); 767 | } 768 | } 769 | const finalLetters = []; 770 | for (const [_, letterData] of letters) { 771 | finalLetters.push(letterData.assignmentExpr); 772 | } 773 | Shuffle(finalLetters); 774 | AddToResult(finalLetters.join(",") + ";"); 775 | if (deobfuscationProtection) { 776 | AddToResult(v_bool_deobfuscationOk + "&&"); 777 | } 778 | AddToResult(v_function_eval + "("); 779 | // Assemble the characters from the input code, using String.fromCharCode 780 | // The maximum number of function arguments is limited (e.g. 65535 on chrome), so for long texts, we need to split up the process 781 | // `batchCount` indicates how many arguments we use for a function call 782 | // 4096 should be fine on all browsers 783 | // Also add random count at a time 784 | const batchCount = 4096; 785 | const batchData = []; 786 | let textIndex = 0; 787 | while (textIndex < inputCharCodes.length) { 788 | const finalTextAssembled = []; 789 | const batchEndIndex = Math.min(textIndex + Math.floor(batchCount * (Random() * 0.5 + 0.5)), inputCharCodes.length); 790 | while (textIndex < batchEndIndex) { 791 | const letterData = letters.get(inputCharCodes[textIndex]); 792 | finalTextAssembled.push(letterData.variableName); 793 | ++textIndex; 794 | } 795 | const currentBatchText = v_function_stringFromCharCode + "(" + finalTextAssembled.join(",") + ")"; 796 | batchData.push(currentBatchText); 797 | } 798 | AddToResult(batchData.join("+")); 799 | AddToResult(")"); 800 | AddToResult("})()"); 801 | return resultSegments.join(""); 802 | } 803 | -------------------------------------------------------------------------------- /dist/main.js: -------------------------------------------------------------------------------- 1 | import { CheckCustomCharacters, Obfuscate, ObfuscateError } from "./lib.js"; 2 | document.title = "j̏ͭͯͤͫ͗͋s͔̣͎̎̑ͭͩ-̣͇ͮͦZ̹͚̫̪͓̠͐ͭ͊̽̾̽ ͍̣̯ͮ̏̂̄ͧͯ̚-͗̍̂ͯ̅̚ ͬ̂ͯ͋J̞̩̠͓͈̳̩ã̰̥̞͚͓v͚̞̯̺͇ḁ̲̹̤͎ͤS̱̰̠̣͚͛̈̌̄̚ͅc͖̥͉̹̮̻̓̊̑ri̭̗̳̩̾p͖͇̟͒t̅̇͋ͅ ̥o̯̗̱b͖̦̹̣̞f̺̥̽̂u͎̖̦̻̻͔̗̐ͥ̂̓̒̅̅s̰̪̞ͫ̿c̲͈̪͓̖ͭ̽â̩͙ͅt̻̺͚ō̝̗͔̗̰r̠̩̤͚̋ͩ"; 3 | const DOM = { 4 | mainPage: document.getElementById("main_page"), 5 | codeInputTextArea: document.getElementById("text_area"), 6 | obfuscateResultTextArea: document.getElementById("result_text"), 7 | zalgoLevelSliderContainer: document.getElementById("zalgo_level_slider_container"), 8 | zalgoLevelText: document.getElementById("zalgo_level_text"), 9 | zalgoLevelSlider: document.getElementById("zalgo_level_slider"), 10 | variableNameLengthSlider: document.getElementById("variable_name_length_slider"), 11 | variableNameLengthText: document.getElementById("variable_name_length_text"), 12 | charsetSelector: document.getElementById("charset_selector"), 13 | zalgoCharsetOption: document.getElementById("type_zalgo"), 14 | invisibleCharsetOption: document.getElementById("type_invisible"), 15 | optionsOverlay: document.getElementById("options_overlay"), 16 | deobfuscationProtectionOptions: document.getElementById("deobfuscation_protection_options"), 17 | customizeCharactersButton: document.getElementById("custom_charset_customizer_button"), 18 | customizeCharactersPage: document.getElementById("customize_page"), 19 | customizeCharactersTextArea: document.getElementById("customize_text_area"), 20 | customCodeEditButton: document.getElementById("custom_code_edit_button"), 21 | customCodeEditPage: document.getElementById("customize_deobfuscation_code_page"), 22 | customCodeEditTextArea: document.getElementById("deobfuscation_custom_code_text_area") 23 | }; 24 | function CopyResult(button) { 25 | button.innerText = "Copied!"; 26 | navigator.clipboard.writeText(DOM.obfuscateResultTextArea.value); 27 | } 28 | function LogarithmicSliderMapValue(sliderMin, sliderMax, base, rawValue) { 29 | const value = (Math.pow(base, rawValue) - 1) / (base - 1); 30 | return Math.round(sliderMin + (sliderMax - sliderMin) * value); 31 | } 32 | function LogarithmicSliderUnmapValue(sliderMin, sliderMax, base, targetValue) { 33 | const t = (targetValue - sliderMin) / (sliderMax - sliderMin); 34 | const x = t * (base - 1) + 1; 35 | return Math.log(x) / Math.log(base); 36 | } 37 | const zalgoLevelSliderMinValue = 0; 38 | const zalgoLevelSliderMaxValue = 100; 39 | const zalgoLevelSliderBase = 10; 40 | let zalgoLevel = 3; 41 | function slider_zalgoLevel(e) { 42 | zalgoLevel = LogarithmicSliderMapValue(zalgoLevelSliderMinValue, zalgoLevelSliderMaxValue, zalgoLevelSliderBase, Number(e.value)); 43 | DOM.zalgoLevelText.textContent = "Zalgo level: " + zalgoLevel.toString(); 44 | } 45 | DOM.zalgoLevelSlider.value = LogarithmicSliderUnmapValue(zalgoLevelSliderMinValue, zalgoLevelSliderMaxValue, zalgoLevelSliderBase, zalgoLevel).toString(); 46 | const variableNameLengthSliderMinValue = 2; 47 | const variableNameLengthSliderMaxValue = 50; 48 | const variableNameLengthSliderBase = 10; 49 | let variableNameLength = 5; 50 | function slider_variableLength(e) { 51 | variableNameLength = LogarithmicSliderMapValue(variableNameLengthSliderMinValue, variableNameLengthSliderMaxValue, variableNameLengthSliderBase, Number(e.value)); 52 | DOM.variableNameLengthText.textContent = "Variable name length: " + variableNameLength.toString(); 53 | } 54 | DOM.variableNameLengthSlider.value = LogarithmicSliderUnmapValue(variableNameLengthSliderMinValue, variableNameLengthSliderMaxValue, variableNameLengthSliderBase, variableNameLength).toString(); 55 | function ShowOptions(show) { 56 | DOM.optionsOverlay.style.display = show ? "flex" : "none"; 57 | DOM.mainPage.style.filter = show ? "blur(3px)" : ""; 58 | } 59 | function ToggleCustomizeMode(on) { 60 | if (!on && !ValidateCustomCharacters()) { 61 | return; 62 | } 63 | DOM.mainPage.style.display = on ? "none" : ""; 64 | DOM.customizeCharactersPage.style.display = on ? "" : "none"; 65 | } 66 | let customCharacters = []; 67 | function ValidateCustomCharacters() { 68 | const characters = [...DOM.customizeCharactersTextArea.value]; 69 | const { startingChars, nonStartingChars, invalidChars } = CheckCustomCharacters(characters); 70 | const invalidCharsDiv = document.getElementById("invalid_chars"); 71 | if (invalidChars.size !== 0) { 72 | const codePointsDivText = Array.from(invalidChars).reduce((acc, curr) => { 73 | const codePoint = curr.codePointAt(0); 74 | if (codePoint === undefined) { 75 | return acc; 76 | } 77 | if (curr === " ") { 78 | curr = "space"; 79 | } 80 | return acc + "
U+" 81 | + codePoint.toString(16).toUpperCase().padStart(4, "0") + " " + curr + "
"; 82 | }, ""); 83 | invalidCharsDiv.innerHTML = "
The following character" + (invalidChars.size === 1 ? "" : "s") 84 | + " cannot be used in variable names:
" + codePointsDivText; 85 | invalidCharsDiv.style.display = ""; 86 | return false; 87 | } 88 | else if (startingChars.length === 0 && nonStartingChars.length !== 0) { 89 | invalidCharsDiv.innerHTML = "
There are no characters that are valid starting characters in variable names
"; 90 | invalidCharsDiv.style.display = ""; 91 | return false; 92 | } 93 | else { 94 | invalidCharsDiv.style.display = "none"; 95 | customCharacters = characters; 96 | return true; 97 | } 98 | } 99 | let charset = "zalgo"; 100 | function CharsetChanged(name) { 101 | switch (name) { 102 | case "zalgo": 103 | case "invisible": 104 | case "iiii": 105 | case "lines": 106 | case "custom": 107 | charset = name; 108 | break; 109 | default: 110 | return; 111 | } 112 | DOM.zalgoLevelSliderContainer.style.display = (charset === "zalgo") ? "" : "none"; 113 | DOM.customizeCharactersButton.style.display = (charset === "custom") ? "" : "none"; 114 | } 115 | let codeGenerationTarget = "es6+"; 116 | function CodeGenerationTargetChanged(target) { 117 | switch (target) { 118 | case "es5-": 119 | case "es6+": 120 | case "nodejs": 121 | codeGenerationTarget = target; 122 | break; 123 | default: 124 | return; 125 | } 126 | const newFeaturesDisabled = codeGenerationTarget === "es5-"; 127 | DOM.zalgoCharsetOption.disabled = newFeaturesDisabled; 128 | DOM.invisibleCharsetOption.disabled = newFeaturesDisabled; 129 | if (newFeaturesDisabled) { 130 | const selectedValue = DOM.charsetSelector.value; 131 | if (selectedValue === "zalgo" || selectedValue === "invisible") { 132 | DOM.charsetSelector.value = "iiii"; 133 | CharsetChanged("iiii"); 134 | } 135 | } 136 | } 137 | let isDeobfuscationProtection = false; 138 | let deobfuscationProtectionMode = "skip"; 139 | function DeobfuscationProtectionChanged(on) { 140 | isDeobfuscationProtection = on; 141 | DOM.deobfuscationProtectionOptions.style.visibility = on ? "" : "hidden"; 142 | DOM.customCodeEditButton.style.display = (deobfuscationProtectionMode === "custom" && on) ? "" : "none"; 143 | } 144 | function DeobfuscationProtectionModeChanged(mode) { 145 | switch (mode) { 146 | case "skip": 147 | case "error": 148 | case "loop": 149 | case "custom": 150 | deobfuscationProtectionMode = mode; 151 | break; 152 | default: 153 | return; 154 | } 155 | DOM.customCodeEditButton.style.display = (mode === "custom" && isDeobfuscationProtection) ? "" : "none"; 156 | } 157 | function ShowCustomCodeEditor(show) { 158 | ShowOptions(false); 159 | DOM.mainPage.style.display = show ? "none" : ""; 160 | DOM.customCodeEditPage.style.display = show ? "" : "none"; 161 | } 162 | function AutoResizeTextarea(textarea, padding) { 163 | textarea.style.height = "auto"; 164 | textarea.style.height = (textarea.scrollHeight - padding * 2) + "px"; 165 | } 166 | function ObfuscateButton() { 167 | const text = DOM.codeInputTextArea.value; 168 | const charsetData = (() => { 169 | switch (charset) { 170 | case "zalgo": return { 171 | type: "zalgo", 172 | zalgoLevel 173 | }; 174 | case "custom": return { 175 | type: "custom", 176 | characters: customCharacters 177 | }; 178 | default: return { 179 | type: charset 180 | }; 181 | } 182 | })(); 183 | const deobfuscationProtection = (() => { 184 | if (!isDeobfuscationProtection) { 185 | return undefined; 186 | } 187 | switch (deobfuscationProtectionMode) { 188 | case "custom": return { 189 | type: "custom", 190 | codeToRun: DOM.customCodeEditTextArea.value 191 | }; 192 | default: return { 193 | type: deobfuscationProtectionMode 194 | }; 195 | } 196 | })(); 197 | let result = ""; 198 | try { 199 | result = Obfuscate(text, { 200 | charset: charsetData, 201 | variableNameLength, 202 | target: codeGenerationTarget, 203 | deobfuscationProtection 204 | }); 205 | } 206 | catch (ex) { 207 | if (ex instanceof ObfuscateError) { 208 | alert(ex.message); 209 | } 210 | return; 211 | } 212 | DOM.obfuscateResultTextArea.value = result; 213 | } 214 | // Set functions on the window object, so that they are callable from html 215 | const windowAny = window; 216 | windowAny.CopyResult = CopyResult; 217 | windowAny.slider_zalgoLevel = slider_zalgoLevel; 218 | windowAny.slider_variableLength = slider_variableLength; 219 | windowAny.ShowOptions = ShowOptions; 220 | windowAny.ToggleCustomizeMode = ToggleCustomizeMode; 221 | windowAny.CharsetChanged = CharsetChanged; 222 | windowAny.CodeGenerationTargetChanged = CodeGenerationTargetChanged; 223 | windowAny.DeobfuscationProtectionChanged = DeobfuscationProtectionChanged; 224 | windowAny.DeobfuscationProtectionModeChanged = DeobfuscationProtectionModeChanged; 225 | windowAny.ShowCustomCodeEditor = ShowCustomCodeEditor; 226 | windowAny.AutoResizeTextarea = AutoResizeTextarea; 227 | windowAny.ObfuscateButton = ObfuscateButton; 228 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | js-Z 7 | 8 | 92 | 93 | 94 | 95 |
96 |
98 | Enter your JavaScript code here:
99 | 100 |
101 | 102 |
103 |
Charset
104 | 111 |
112 | 114 | 115 |
116 |
Variable name length: 5
117 | 119 |
120 |
121 |
Zalgo level: 3
122 | 124 |
125 |
126 | 127 |
128 | 130 | 132 |
133 |
134 | 174 | 183 | 200 | 201 | 203 | 204 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /lib.ts: -------------------------------------------------------------------------------- 1 | 2 | // Zalgo characters, see https://eeemo.net/ 3 | interface ZalgoCharset 4 | { 5 | type: "zalgo"; 6 | zalgoLevel?: number; // How many additional zalgo characters to append to each character 7 | } 8 | 9 | // Characters that are not rendered on most text editors/browsers: 10 | // U+FE00 - U+FE0F Variation Selector 11 | interface InvisibleCharset 12 | { 13 | type: "invisible"; 14 | } 15 | 16 | // "I" characters that look the same: 17 | // U+0049 Latin Capital letter I 18 | // U+0399 Greek Capital Letter Iota 19 | // U+0406 Cyrillic Capital Letter Byelorussian-Ukrainian I 20 | interface IIIICharset 21 | { 22 | type: "iiii"; 23 | } 24 | 25 | // Characters that are made out of lines (or something like that) 26 | interface LinesCharset 27 | { 28 | type: "lines"; 29 | } 30 | 31 | // User-defined characters 32 | interface CustomCharset 33 | { 34 | type: "custom"; 35 | 36 | // Custom characters to use in variable names. 37 | // Characters can actually be more more than one character long; 38 | // in that case, the entire string will be used as part of the variable name. 39 | // Empty strings are skipped. 40 | characters: string[]; 41 | } 42 | 43 | export type Charset = ZalgoCharset | InvisibleCharset | IIIICharset | LinesCharset | CustomCharset; 44 | 45 | export type CharsetType = Charset["type"]; 46 | 47 | export type CodeGenerationTarget = 48 | | "es6+" // Modern browsers, https://caniuse.com/es6 49 | | "nodejs" // Node.js - this usually also works in browsers using the V8 javascript engine (mostly chromium based browsers) 50 | | "es5-" // Old browsers, ES5 or earlier 51 | ; 52 | 53 | // If the obfuscated code is deobfuscated, then don't run the original code at all 54 | interface DeobfuscationProtectionModeSkip 55 | { 56 | type: "skip"; 57 | } 58 | 59 | // If the code is deobfuscated, then throw an error 60 | interface DeobfuscationProtectionModeThrowError 61 | { 62 | type: "error"; 63 | } 64 | 65 | // If the code is deobfuscated, then run an infinite loop (which usually freezes the page) 66 | interface DeobfuscationProtectionModeInfiniteLoop 67 | { 68 | type: "loop"; 69 | } 70 | 71 | // If the code is deobfuscated, then run a user-provided code 72 | interface DeobfuscationProtectionModeRunCustomCode 73 | { 74 | type: "custom"; 75 | codeToRun: string; // The code to run when deobfuscated 76 | } 77 | 78 | export type DeobfuscationProtection = 79 | | DeobfuscationProtectionModeSkip 80 | | DeobfuscationProtectionModeThrowError 81 | | DeobfuscationProtectionModeInfiniteLoop 82 | | DeobfuscationProtectionModeRunCustomCode 83 | ; 84 | 85 | export type DeobfuscationProtectionType = DeobfuscationProtection["type"]; 86 | 87 | export interface ObfuscationOptions 88 | { 89 | charset?: Charset; // Type of the character set to use, see the comments for each character set for details 90 | variableNameLength?: number; // How long should variable names be in the generated code 91 | target?: CodeGenerationTarget; // Which version of JavaScript to target 92 | deobfuscationProtection?: DeobfuscationProtection; // Describes what should happen if the code is deobfuscated 93 | rngSeed?: number; // Number to use when seeding the random number generator (same seed == same code output). Must be in range [0, 2^32-1] 94 | } 95 | 96 | export const enum ObfuscateErrorType 97 | { 98 | CannotGetVariableName, 99 | CannotGetVariableNameCustom, 100 | CharsetIsIncompatibleES5, 101 | HasInvalidCustomCharacters 102 | } 103 | 104 | const errorMessages: { [key in ObfuscateErrorType]: string } = { 105 | [ObfuscateErrorType.CannotGetVariableName]: "Cannot get a variable name, try increasing the variable name length", 106 | [ObfuscateErrorType.CannotGetVariableNameCustom]: "Cannot get a variable name, try adding more custom characters or increase the variable name length", 107 | [ObfuscateErrorType.CharsetIsIncompatibleES5]: "The selected charset is not supported for es5 target", 108 | [ObfuscateErrorType.HasInvalidCustomCharacters]: "Some of the custom characters cannot be used in variable names" 109 | }; 110 | 111 | export class ObfuscateError 112 | { 113 | public readonly type: ObfuscateErrorType; 114 | public readonly message: string; 115 | public readonly invalidCharacters: Set | null; // Not null if type is `HasInvalidCustomCharacters` 116 | 117 | constructor(type: ObfuscateErrorType, invalidCharacters?: Set) 118 | { 119 | this.type = type; 120 | this.message = errorMessages[type]; 121 | this.invalidCharacters = invalidCharacters ?? null; 122 | } 123 | } 124 | 125 | interface ObfuscationOptionsInternal 126 | { 127 | charset: Charset; 128 | variableNameLength: number; 129 | target: CodeGenerationTarget; 130 | deobfuscationProtection: DeobfuscationProtection | null; 131 | } 132 | 133 | // Simple random number generator with seed 134 | function Mulberry32(seed: number) 135 | { 136 | return () => 137 | { 138 | let t = seed += 0x6D2B79F5; 139 | t = Math.imul(t ^ t >>> 15, t | 1); 140 | t ^= t + Math.imul(t ^ t >>> 7, t | 61); 141 | return ((t ^ t >>> 14) >>> 0) / 4294967296; 142 | }; 143 | } 144 | 145 | let Random = Math.random; 146 | 147 | function Shuffle(array: Array) 148 | { 149 | let current = array.length; 150 | let temp; 151 | let rand; 152 | 153 | while (current !== 0) 154 | { 155 | rand = Math.floor(Random() * current); 156 | --current; 157 | 158 | temp = array[current]; 159 | array[current] = array[rand]; 160 | array[rand] = temp; 161 | } 162 | } 163 | 164 | function Urand(min: number, max?: number) 165 | { 166 | if (max === undefined) 167 | { 168 | return Math.floor(Random() * min); 169 | } 170 | 171 | return Math.floor(Random() * max - min) + min; 172 | } 173 | 174 | function RandomElementsOfStringArray(array: string[], count: number) 175 | { 176 | let ret = ""; 177 | for (let i = 0; i < count; ++i) 178 | { 179 | ret += array[Urand(array.length)]; 180 | } 181 | 182 | return ret; 183 | } 184 | 185 | function RandomElementOf(array: T[]) 186 | { 187 | return array[Urand(array.length)]; 188 | } 189 | 190 | function IsValidName(name: string) 191 | { 192 | try 193 | { 194 | eval("var " + name); 195 | return true; 196 | } 197 | catch (e) 198 | { 199 | return false; 200 | } 201 | } 202 | 203 | const zalgoCharsExtended = [ 204 | "\u08f1", "\u0363", "\u0364", "\u0365", "\u0366", "\u0367", "\u0368", "\u0369", 205 | "\u036a", "\u036b", "\u036c", "\u036d", "\u036e", "\u036f", "\u0483", "\u0484", 206 | "\u0485", "\u0486", "\u0593", "\u0594", "\u0595", "\u0597", "\u0598", "\u059c", 207 | "\u059e", "\u059f", "\u05a0", "\u05a1", "\u05a8", "\u05a9", "\u05ab", "\u05ac", 208 | "\u05af", "\u05b5", "\u05c4", "\u0610", "\u0611", "\u0612", "\u0613", "\u0614", 209 | "\u0615", "\u0616", "\u0617", "\u0618", "\u0619", "\u061a", "\u0656", "\u0657", 210 | "\u0658", "\u065a", "\u065b", "\u065d", "\u065e", "\u065f", "\u0674", "\u06d6", 211 | "\u06d7", "\u06d8", "\u06d9", "\u06da", "\u06db", "\u06dc", "\u06df", "\u06e0", 212 | "\u06e1", "\u06e2", "\u06e3", "\u06e4", "\u06e7", "\u06e8", "\u06ea", "\u06eb", 213 | "\u06ec", "\u06ed", "\u08e4", "\u08e5", "\u08e6", "\u08e7", "\u08e8", 214 | "\u08e9", "\u08f0", "\u08f2", "\u08f3", "\u08f4", "\u08f5", "\u08f6", "\u08f7", 215 | "\u08f8", "\u08f9", "\u08fa", "\u08fb", "\u08fc", "\u08fd", "\u08fe", "\u0e31", 216 | "\u0e34", "\u0e35", "\u0e36", "\u0e37", "\u0e38", "\u0e39", "\u0e3a", "\u0e47", 217 | "\u0e48", "\u0e49", "\u0e4a", "\u0e4b", "\u0e4c", "\u0e4d", "\u0e4e", "\u0ec9", 218 | "\u0f19", "\u0f35", "\u0f37", "\u0f72", "\u0f7a", "\u0f7b", "\u0f7c", "\u0f7d", 219 | "\u0f80", "\u0f84", "\u1dc0", "\u1dc1", "\u1dc2", "\u1dc3", "\u1dc4", "\u1dc5", 220 | "\u1dc6", "\u1dc7", "\u1dc8", "\u1dc9", "\u1dca", "\u1dfe", "\u1dff", "\ufb1e", 221 | "\ufc5e", "\ufc5f", "\ufc60", "\ufc61", "\ufc62", "\ufc63" 222 | ]; 223 | 224 | // Stolen from http://eeemo.net/ 225 | const zalgo_up = [ 226 | '\u030d', '\u030e', '\u0304', '\u0305', 227 | '\u033f', '\u0311', '\u0306', '\u0310', 228 | '\u0352', '\u0357', '\u0351', '\u0307', 229 | '\u0308', '\u030a', '\u0342', '\u0343', 230 | '\u0344', '\u034a', '\u034b', '\u034c', 231 | '\u0303', '\u0302', '\u030c', '\u0350', 232 | '\u0300', '\u0301', '\u030b', '\u030f', 233 | '\u0312', '\u0313', '\u0314', '\u033d', 234 | '\u0309', '\u0363', '\u0364', '\u0365', 235 | '\u0366', '\u0367', '\u0368', '\u0369', 236 | '\u036a', '\u036b', '\u036c', '\u036d', 237 | '\u036e', '\u036f', '\u033e', '\u035b', 238 | '\u0346', '\u031a' 239 | ]; 240 | 241 | const zalgo_down = [ 242 | '\u0316', '\u0317', '\u0318', '\u0319', 243 | '\u031c', '\u031d', '\u031e', '\u031f', 244 | '\u0320', '\u0324', '\u0325', '\u0326', 245 | '\u0329', '\u032a', '\u032b', '\u032c', 246 | '\u032d', '\u032e', '\u032f', '\u0330', 247 | '\u0331', '\u0332', '\u0333', '\u0339', 248 | '\u033a', '\u033b', '\u033c', '\u0345', 249 | '\u0347', '\u0348', '\u0349', '\u034d', 250 | '\u034e', '\u0353', '\u0354', '\u0355', 251 | '\u0356', '\u0359', '\u035a', '\u0323' 252 | ]; 253 | 254 | const zalgo_mid = [ 255 | '\u0315', '\u031b', '\u0340', '\u0341', 256 | '\u0358', '\u0321', '\u0322', '\u0327', 257 | '\u0328', '\u0334', '\u0335', '\u0336', 258 | '\u034f', '\u035c', '\u035d', '\u035e', 259 | '\u035f', '\u0360', '\u0362', '\u0338', 260 | '\u0337', '\u0361' 261 | ]; 262 | 263 | const standaloneChars = [ 264 | "\u13B0", "\u13B1", "\u2C0D", "\u2CB2", 265 | "\u2CBA", "\uA722", "\uA724", "\uA78B", 266 | 267 | "\u02B9", "\u02BA", "\u02BB", "\u02BC", 268 | "\u02BD", "\u02BE", "\u02BF", "\u02C6", 269 | "\u02C7", "\u02C8", "\u02C9", "\u02CA", 270 | "\u02CB", "\u02CC", "\u02CE", "\u02CF", 271 | "\u02D0", "\u02D1", "\u02EC", "\u0374", 272 | "\u037A", "\u0559", "\u0640", "\u07F4", 273 | "\u07F5", "\u07FA", "\u0971", "\u1843", 274 | "\u1C78", "\u1C79", "\u1C7A", "\u1C7B", 275 | "\u1C7C", "\u1D54", "\u1D55", "\u2D6F", 276 | "\u3035", "\u3033", "\u309D", 277 | "\u30FC", "\u30FD", "\u30FE", "\uA4F8", 278 | "\uA4F9", "\uA4FA", "\uA4FB", "\uA4FC", 279 | "\uA4FD", "\uA67F", "\uA788", "\uFF70", 280 | "\uFF9E", "\uFF9F", "\uA9E6", 281 | "\u05D9", "\u05D5", "\u0621", "\u0629", 282 | "\u0647", "\u06C0", "\u06C1", "\u06C2", 283 | "\u06C3", "\u06D5", "\u0715", "\u0716", 284 | "\u0718", "\u0719", "\u071A", "\u071D", 285 | "\u0722", "\u072A", "\u072F", "\u074D", 286 | "\u075A", "\u0765", "\u0766", "\u0780", 287 | "\u0789", "\u0799", "\u079A", "\u07D1", 288 | "\u07EA", "\u0B83", "\u0B9F", "\u0CF2", 289 | "\u0E14", "\u0E15", "\u0E16", "\u0E23", 290 | "\u0E27", "\u0E32", "\u0E33", "\u0E40", 291 | "\u0E45", "\u0EB0", "\u0EB2", 292 | "\u0EC0", "\u0F44", "\u0F4B", "\u0F50", 293 | "\u0F54", "\u0F55", "\u0F56", "\u0F66", 294 | "\u113C", "\u113E", "\u1173", "\u119E", 295 | "\u11A2", "\u141D", "\u141F", "\u1420", 296 | "\u1425", "\u1426", "\u1427", "\u1428", 297 | "\u1429", "\u1449", "\u144A", "\u14BB", 298 | "\u14BC", "\u14BD", "\u14BE", "\u14D0", 299 | "\u1508", "\u1540", "\u153E", 300 | ]; 301 | 302 | const startingChars = [ 303 | "\u02b9", "\u02ba", "\u02bb", "\u02bc", "\u02bd", "\u02be", "\u02bf", "\u02c6", 304 | "\u02c7", "\u02c8", "\u02cc", "\u02d1", "\u0374", "\u037a", "\u0559", "\u1843", 305 | "\u1c78", "\u1c79", "\u1c7a", "\u1c7c", "\u1d54", "\u1d55", "\u1da5", 306 | "\u309d", "\u30fd", "\ua4f8", "\ua4f9", "\ua4fa", "\ua4fb", "\ua4fc", "\ua4fd", 307 | "\ua67f", "\ua717", "\ua718", "\ua719", "\ua71a", "\ua788", "\uff9e", "\uff9f", 308 | "\ua9e6", "\u05d9", "\u0621", "\u0627", "\u0674", "\u06c0", "\u06c1", "\u06c2", 309 | "\u06c3", "\u06d5", "\u0715", "\u0716", "\u0719", "\u071d", "\u072a", "\u072f", 310 | "\u0780", "\u0787", "\u0789", "\u0799", "\u079a", "\u07a2", "\u07a3", "\u07d1", 311 | "\u07e8", "\u0eb2", "\u03b3", "\u0f44", "\u0f4b", "\u0f56", "\u0f8b", "\u141d", 312 | "\u141e", "\u141f", "\u1420", "\u1425", "\u1426", "\u1427", "\u1449", "\u14a1", 313 | "\u14a2", "\u14bb", "\u14bc", "\u14bd", "\u14d0", "\u14d2", "\u14ea", "\u1505", 314 | "\u1507", "\u1508", "\u1509", "\u150a", "\u153e", "\u1550", "\u155d", "\u156a", 315 | "\u1595", "\u159f", "\u15a6", "\u15ae", "\u1690", "\u1691", "\u1692", "\u1693", 316 | "\u1694", "\u16b2", "\u16cd", "\u16e7", "\u1822", "\u1823", "\u1824", "\u1825", 317 | "\u1826", "\u1827", "\u1829", "\u182b", "\u1830", "\u1831", "\u1832", "\u1833", 318 | "\u1834", "\u1835", "\u1836", "\u1837", "\u1838", "\u1839", "\u183a", "\u183b", 319 | "\u184d", "\u1855", "\u185f", "\u186a", "\u188f", "\u189c", "\u18a4", "\u18a5", 320 | "\u18a6", "\u18d9", "\u18da", "\u18de", "\u18df", "\u2d30", "\u2d3e", "\u2d42", 321 | "\u2d46", "\u2d48", "\u2d53", "\u2d57", "\u318d", "\u31b4", "\u31b5", "\u31b6" 322 | ]; 323 | 324 | const baseChars = [ 325 | "\u02b9", "\u02ba", "\u02bb", "\u02bc", "\u02bd", "\u02be", "\u02bf", "\u02c6", 326 | "\u02c7", "\u02c8", "\u02cc", "\u02d1", "\u0374", "\u037a", "\u0559", "\u1d54", 327 | "\u1d55", "\u1da5", "\ua4f8", "\ua4f9", "\ua4fa", "\ua4fb", "\ua4fc", "\ua4fd", 328 | "\ua67f", "\ua717", "\ua718", "\ua719", "\ua71a", "\ua788", "\u05d9", "\u0621", 329 | "\u0627", "\u0674", "\u06c0", "\u06c1", "\u06c2", "\u06c3", "\u06d5", "\u03b3" 330 | ]; 331 | 332 | const linesLetters = [ 333 | "\u13B1", "\u13DD", 334 | "\u13DE", "\u13DF", 335 | "\u2C84", "\u2CA4", 336 | "\u2CB6", "\u2CBA", 337 | "\u2CD0", "\u2CDE", 338 | "\uA78B", "\u{10411}", 339 | "\u{10415}", "\u{1041B}", 340 | "\u{10423}", "\u{1D5A2}", 341 | "\u{1D5A8}", "\u{1D5A9}", 342 | "\u{1D5AB}", "\u{1D5D6}", 343 | "\u{1D5DC}", "\u{1D5DD}", 344 | "\u{1D5DF}", "\u{1D758}", 345 | "\u{1D75E}", "\u{1D763}", 346 | "\u2C85", "\u2C93", 347 | "\u2CA5", "\u2CB7", 348 | "\u2CBB", "\u2CDF", 349 | "\uA78C", "\u{10439}", 350 | "\u{1043D}", "\u{10443}", 351 | "\u{1044B}", "\u{1D5C5}", 352 | "\u{1D5F9}", "\uAB81", 353 | "\uABAE", "\uABAF", 354 | "\u02B9", "\u02BB", 355 | "\u02BC", "\u02BD", 356 | "\u02C8", "\u02C9", 357 | "\u02CA", "\u02CB", 358 | "\u02CC", "\u02CD", 359 | "\u02CE", "\u02CF", 360 | "\u0374", "\u0559", 361 | "\u07F4", "\u07F5", 362 | "\u1C7B", "\u1C7C", 363 | "\uA4F9", "\uA60C", 364 | "\uFF70", "\u01C0", 365 | "\u01C1", "\u0B9F", 366 | "\u1100", "\u1102", 367 | "\u115F", "\u1160", 368 | "\u1173", "\u1175", 369 | "\u119E", "\u11A1", 370 | "\u11A8", "\u11AB", 371 | "\u11FA", "\u1427", 372 | "\u1428", "\u1429", 373 | "\u1433", "\u1438", 374 | "\u1440", "\u1441", 375 | "\u1444", "\u1445", 376 | "\u1449", "\u144A", 377 | "\u14A3", "\u14A5", 378 | "\u14A7", "\u14AA", 379 | "\u14AC", "\u14AF", 380 | "\u14B2", "\u14B7", 381 | "\u1540", "\u167F", 382 | "\u16C1", "\u16CC", 383 | "\u16E7", "\u18B4", 384 | "\u18B5", "\u18B6", 385 | "\u18DE", "\u18DF", 386 | "\u1963", "\u2D4E", 387 | "\u2D4F", "\u30CB", 388 | "\u30FF", "\u3127", 389 | "\u3131", "\u3134", 390 | "\u3137", "\u3161", 391 | "\u3163", "\u318D", 392 | "\u318E", 393 | 394 | //"\uFF9E", "\uFF9F", 395 | 396 | ]; 397 | 398 | const linesLetters_compatibility = [ 399 | "\u13b1", "\u13dd", "\u13de", "\u13df", 400 | "\u2c84", "\u2ca4", "\u2cb6", "\u2cba", 401 | "\u2cd0", "\u2cde", "\ua78b", "\u2c85", 402 | "\u2c93", "\u2ca5", "\u2cb7", "\u2cbb", 403 | "\u2cdf", "\ua78c", "\u02bb", "\u02bc", 404 | "\u02bd", "\u0559", "\u07f4", "\u07f5", 405 | "\u1c7b", "\u1c7c", "\ua60c", "\uff70", 406 | "\u01c0", "\u01c1", "\u0b9f", "\u1100", 407 | "\u1102", "\u115f", "\u1160", "\u1173", 408 | "\u1175", "\u119e", "\u11a1", "\u11a8", 409 | "\u11ab", "\u1427", "\u1428", "\u1429", 410 | "\u1433", "\u1438", "\u1440", "\u1441", 411 | "\u1444", "\u1445", "\u1449", "\u144a", 412 | "\u14a3", "\u14a5", "\u14a7", "\u14aa", 413 | "\u14ac", "\u14af", "\u14b2", "\u14b7", 414 | "\u1540", "\u16c1", "\u16cc", "\u16e7", 415 | "\u1963", "\u2d4e", "\u2d4f", "\u30cb", 416 | "\u30ff", "\u3127", "\u3131", "\u3134", 417 | "\u3137", "\u3161", "\u3163", "\u318d", 418 | "\u318e" 419 | ]; 420 | 421 | const iLetters = ["\u0049", "\u0399", "\u0406"]; 422 | 423 | interface CustomCharacterData 424 | { 425 | startingChars: string[]; 426 | nonStartingChars: string[]; 427 | } 428 | 429 | function Unreachable(_: never): never 430 | { 431 | throw Error("Unreachable"); 432 | } 433 | 434 | export function CheckCustomCharacters(characters: string[]) 435 | { 436 | // Unique characters 437 | const startingChars = new Set(); 438 | const nonStartingChars = new Set(); 439 | const invalidChars = new Set(); 440 | 441 | // Same characters as above, but characters can appear multiple times 442 | const allNonStartingChars: string[] = []; 443 | const allStartingChars: string[] = []; 444 | 445 | // These can cause `"var a" + char` to be valid code, so these are skipped early 446 | const badCharacters = ["\n", "\r", "\t", " ", ";"]; 447 | 448 | for (const char of characters) 449 | { 450 | if (char.length === 0) 451 | { 452 | // Skip empty 453 | continue; 454 | } 455 | 456 | if (badCharacters.includes(char)) 457 | { 458 | // Skip bad character 459 | invalidChars.add(char); 460 | continue; 461 | } 462 | 463 | if (char.length !== 1) 464 | { 465 | // Longer "character", must not contain any of the bad characters 466 | let valid = true; 467 | for (const ch of char) 468 | { 469 | if (badCharacters.includes(ch)) 470 | { 471 | valid = false; 472 | break; 473 | } 474 | } 475 | 476 | if (!valid) 477 | { 478 | invalidChars.add(char); 479 | continue; 480 | } 481 | } 482 | 483 | if (invalidChars.has(char)) 484 | { 485 | // Already checked, invalid 486 | continue; 487 | } 488 | 489 | if (nonStartingChars.has(char)) 490 | { 491 | // Already checked, valid 492 | // Add again to increase chance 493 | allNonStartingChars.push(char); 494 | continue; 495 | } 496 | 497 | if (startingChars.has(char)) 498 | { 499 | // Same as before, but for starting characters 500 | allStartingChars.push(char); 501 | continue; 502 | } 503 | 504 | try 505 | { 506 | // Check if the current character is valid as a starting character in a variable name 507 | eval("var " + char); 508 | 509 | // If no exception is thrown, then it's valid 510 | startingChars.add(char); 511 | allStartingChars.push(char); 512 | } 513 | catch { } 514 | 515 | try 516 | { 517 | // Check again, but as a non-starting character 518 | eval("var a" + char); 519 | nonStartingChars.add(char); 520 | allNonStartingChars.push(char); 521 | } 522 | catch 523 | { 524 | // The current character cannot be used 525 | invalidChars.add(char); 526 | } 527 | } 528 | 529 | return { 530 | startingChars: allStartingChars, 531 | nonStartingChars: allNonStartingChars, 532 | invalidChars, 533 | }; 534 | } 535 | 536 | let debugVarIndex = 0; 537 | let debuggingVarNames = false; 538 | 539 | function GetRandomVariableName(length: number, allowLonger: boolean, options: ObfuscationOptionsInternal, customCharacterData: CustomCharacterData) 540 | { 541 | if (debuggingVarNames) 542 | { 543 | return "a_" + (debugVarIndex++); 544 | } 545 | 546 | const { charset } = options; 547 | 548 | let name = ""; 549 | switch (charset.type) 550 | { 551 | case "zalgo": 552 | { 553 | name = RandomElementOf(startingChars); 554 | const zalgoLevel = charset.zalgoLevel ?? 3; 555 | while (name.length < length) 556 | { 557 | if (zalgoLevel === 0) 558 | { 559 | name += RandomElementOf(standaloneChars); 560 | } 561 | else 562 | { 563 | if (Random() < 0.9) 564 | { 565 | // Add a random number of zalgo characters with a starting character 566 | name += RandomElementOf(baseChars); 567 | 568 | const min = zalgoLevel; 569 | const max = zalgoLevel * 1.5; 570 | const diff = max - min; 571 | 572 | name += RandomElementsOfStringArray(zalgo_up, Math.floor(Random() * diff + min)); 573 | name += RandomElementsOfStringArray(zalgo_mid, Math.floor(Random() * diff + min)); 574 | name += RandomElementsOfStringArray(zalgo_down, Math.floor(Random() * diff + min)); 575 | continue; 576 | } 577 | else 578 | { 579 | // Add a random extended zalgo character with a starting character 580 | // The only character that actually works and is not a letter is _ 581 | name += "_"; 582 | const count = Urand(zalgoLevel, zalgoLevel * 1.5); 583 | const ch = RandomElementOf(zalgoCharsExtended); 584 | 585 | for (let j = 0; j < count; ++j) 586 | { 587 | name += ch; 588 | } 589 | 590 | continue; 591 | } 592 | } 593 | } 594 | 595 | break; 596 | } 597 | case "lines": 598 | { 599 | name = RandomElementsOfStringArray(options.target === "es5-" ? linesLetters_compatibility : linesLetters, length); 600 | break; 601 | } 602 | case "invisible": 603 | { 604 | name = "\u02cb"; 605 | for (let i = 0; i < length; ++i) 606 | { 607 | name += String.fromCharCode(0xfe00 + (Random() * 16) | 0); 608 | } 609 | 610 | break; 611 | } 612 | case "iiii": 613 | { 614 | name = RandomElementsOfStringArray(iLetters, length); 615 | break; 616 | } 617 | case "custom": 618 | { 619 | name = 620 | RandomElementsOfStringArray(customCharacterData.startingChars, 1) + 621 | RandomElementsOfStringArray(customCharacterData.nonStartingChars, length - 1); 622 | 623 | break; 624 | } 625 | default: 626 | Unreachable(charset); 627 | } 628 | 629 | if (allowLonger) 630 | { 631 | return name; 632 | } 633 | 634 | // Trim name to the given length 635 | 636 | const chars = [...name].slice(0, length); 637 | return chars.join(""); 638 | } 639 | 640 | function GetNewVariableName(usedVariableNames: Set, options: ObfuscationOptionsInternal, customCharacterData: CustomCharacterData) 641 | { 642 | const { variableNameLength, charset } = options; 643 | const allowLongerName = charset.type === "zalgo"; 644 | 645 | let len: number; 646 | if (allowLongerName) 647 | { 648 | const min = variableNameLength; 649 | const max = variableNameLength * 1.5; 650 | len = Math.floor(Random() * (max - min) + min); 651 | } 652 | else 653 | { 654 | len = variableNameLength; 655 | } 656 | 657 | const maxAttempts = 100; 658 | for (let i = 0; i < maxAttempts; ++i) 659 | { 660 | const name = GetRandomVariableName(len, allowLongerName, options, customCharacterData); 661 | if (!IsValidName(name)) 662 | { 663 | // Should not happen, was already checked before 664 | continue; 665 | } 666 | 667 | if (!usedVariableNames.has(name)) 668 | { 669 | // Found a new variable name which has not been used yet 670 | usedVariableNames.add(name); 671 | return name; 672 | } 673 | } 674 | 675 | // Could not get new variable name 676 | 677 | if (charset.type === "custom") 678 | { 679 | throw new ObfuscateError(ObfuscateErrorType.CannotGetVariableNameCustom); 680 | } 681 | else 682 | { 683 | throw new ObfuscateError(ObfuscateErrorType.CannotGetVariableName); 684 | } 685 | } 686 | 687 | function GetCharCodes(str: string) 688 | { 689 | // Note: this will split characters with char code >0xffff into two characters 690 | // But those will be re-combined after string concatenation, so it will work fine 691 | // By not using code points, we only need to go to 32768 for the highest number bit, instead of 524288, which saves some space 692 | 693 | // return str.split("").map(ch => ch.charCodeAt(0)); 694 | const codes = new Array(str.length); 695 | for (let i = 0; i < str.length; ++i) 696 | { 697 | codes[i] = str.charCodeAt(i); 698 | } 699 | 700 | return codes; 701 | } 702 | 703 | function GetIntFromBits(number: number, i0: string, i1: string, i2: string, i4: string, i8: string, i16: string, i32: string, i64: string, 704 | i128: string, i256: string, i512: string, i1024: string, i2048: string, i4096: string, i8192: string, i16384: string, i32768: string) 705 | { 706 | // Construct a number from bits, using the given variable names 707 | // e.g. the number 20 can be constructed from 4 + 16 708 | 709 | if (number === 0) 710 | { 711 | return i0; 712 | } 713 | 714 | const ret: string[] = []; 715 | 716 | const numberNames = [i1, i2, i4, i8, i16, i32, i64, i128, i256, i512, i1024, i2048, i4096, i8192, i16384, i32768]; 717 | for (const num of numberNames) 718 | { 719 | if (number & 1) 720 | { 721 | ret.push(num); 722 | } 723 | 724 | number >>= 1; 725 | } 726 | 727 | Shuffle(ret); 728 | 729 | let retStr = ret[0]; // Should always have at least one element 730 | 731 | for (let i = 1; i < ret.length; ++i) 732 | { 733 | // Use + or | at random; since the numbers are powers of 2, both have the same effect 734 | retStr += Random() < 0.5 ? "+" : "|"; 735 | retStr += ret[i]; 736 | } 737 | 738 | return retStr; 739 | } 740 | 741 | function ValidateRngSeed(seed: number) 742 | { 743 | if (Number.isNaN(seed) || !Number.isFinite(seed)) 744 | { 745 | return 0; 746 | } 747 | else if (seed < 0) 748 | { 749 | return 0; 750 | } 751 | else if (seed > 0xffffffff) 752 | { 753 | return 0xffffffff; 754 | } 755 | 756 | return Math.floor(seed); 757 | } 758 | 759 | // Returns the obfuscated version of the input code 760 | // Throws `ObfuscateError` if the code could not be obfuscated for some reason 761 | // See comments for `ObfuscationOptions` for more information about the options (everything is optional) 762 | export function Obfuscate(inputCode: string, options?: ObfuscationOptions): string 763 | { 764 | if (inputCode === "") 765 | { 766 | return ""; 767 | } 768 | 769 | const optionsValidated: ObfuscationOptionsInternal = { 770 | charset: options?.charset ?? { type: "zalgo" }, 771 | variableNameLength: Math.max(options?.variableNameLength ?? 2, 2), 772 | target: options?.target ?? "es6+", 773 | deobfuscationProtection: options?.deobfuscationProtection ?? null 774 | }; 775 | 776 | const rngSeed = options?.rngSeed ?? null; 777 | Random = (rngSeed === null) ? Math.random : Mulberry32(ValidateRngSeed(rngSeed)); 778 | 779 | const { charset, variableNameLength, target, deobfuscationProtection } = optionsValidated; 780 | 781 | if (target === "es5-") 782 | { 783 | if (charset.type === "zalgo" || charset.type === "invisible") 784 | { 785 | throw new ObfuscateError(ObfuscateErrorType.CharsetIsIncompatibleES5); 786 | } 787 | } 788 | 789 | // Check custom characters, or just use an empty array if the charset is not custom 790 | const customCharacterData = CheckCustomCharacters(charset.type === "custom" ? charset.characters : []); 791 | if (customCharacterData.invalidChars.size !== 0) 792 | { 793 | throw new ObfuscateError(ObfuscateErrorType.HasInvalidCustomCharacters, customCharacterData.invalidChars); 794 | } 795 | 796 | const usedVariableNames = new Set(); 797 | function GetVariableName() 798 | { 799 | return GetNewVariableName(usedVariableNames, optionsValidated, customCharacterData); 800 | } 801 | 802 | const resultSegments: string[] = []; 803 | function AddToResult(str: string) 804 | { 805 | resultSegments.push(str); 806 | } 807 | 808 | AddToResult("(" + (target === "es5-" ? "function()" : "()=>") + "{var "); 809 | 810 | const v_number_0 = GetVariableName(); 811 | const v_number_1 = GetVariableName(); 812 | const v_number_2 = GetVariableName(); 813 | const v_number_4 = GetVariableName(); 814 | const v_number_8 = GetVariableName(); 815 | const v_number_16 = GetVariableName(); 816 | const v_number_32 = GetVariableName(); 817 | const v_number_64 = GetVariableName(); 818 | const v_number_128 = GetVariableName(); 819 | const v_number_256 = GetVariableName(); 820 | const v_number_512 = GetVariableName(); 821 | const v_number_1024 = GetVariableName(); 822 | const v_number_2048 = GetVariableName(); 823 | const v_number_4096 = GetVariableName(); 824 | const v_number_8192 = GetVariableName(); 825 | const v_number_16384 = GetVariableName(); 826 | const v_number_32768 = GetVariableName(); 827 | 828 | // Generate variables for each letter in the input code 829 | const letters = new Map(); 830 | 831 | function GetNumber(num: number) 832 | { 833 | return GetIntFromBits(num, 834 | v_number_0, v_number_1, v_number_2, v_number_4, v_number_8, v_number_16, v_number_32, 835 | v_number_64, v_number_128, v_number_256, v_number_512, v_number_1024, v_number_2048, 836 | v_number_4096, v_number_8192, v_number_16384, v_number_32768) 837 | } 838 | 839 | const inputCharCodes = GetCharCodes(inputCode); 840 | 841 | // Collect unique characters from the input text 842 | for (const ch of inputCharCodes) 843 | { 844 | if (letters.has(ch)) 845 | { 846 | continue; 847 | } 848 | 849 | const variableName = GetVariableName(); 850 | letters.set(ch, { variableName, assignmentExpr: variableName + "=" + GetNumber(ch) }); 851 | } 852 | 853 | const allowLongerName = charset.type === "zalgo"; 854 | 855 | function GetRandomStr(allowLonger: boolean) 856 | { 857 | return GetRandomVariableName(variableNameLength, allowLonger, optionsValidated, customCharacterData); 858 | } 859 | 860 | AddToResult(v_number_0 + "='" + GetRandomStr(allowLongerName) + "'&'" + GetRandomStr(allowLongerName) + "',"); 861 | AddToResult(v_number_1 + "=-~'" + GetRandomStr(allowLongerName) + "',"); 862 | AddToResult(v_number_2 + "=" + v_number_1 + "-~'" + GetRandomStr(false) + "',"); 863 | AddToResult(v_number_4 + "=" + v_number_2 + "+" + v_number_2 + ","); 864 | AddToResult(v_number_8 + "=" + v_number_4 + "*" + v_number_2 + ","); 865 | AddToResult(v_number_16 + "=" + v_number_2 + "*" + v_number_4 + "*" + v_number_2 + ","); 866 | AddToResult(v_number_32 + "=" + v_number_8 + "*" + v_number_4 + ","); 867 | AddToResult(v_number_64 + "=" + v_number_4 + "*" + v_number_16 + ","); 868 | AddToResult(v_number_128 + "=" + v_number_8 + "*" + v_number_2 + "*" + v_number_8 + ","); 869 | AddToResult(v_number_256 + "=" + v_number_64 + "*" + v_number_4 + ","); 870 | AddToResult(v_number_512 + "=" + v_number_8 + "*" + v_number_64 + ","); 871 | AddToResult(v_number_1024 + "=" + v_number_4 + "*" + v_number_256 + ","); 872 | AddToResult(v_number_2048 + "=" + v_number_64 + "*" + v_number_32 + ","); 873 | AddToResult(v_number_4096 + "=" + v_number_256 + "*" + v_number_16 + ","); 874 | AddToResult(v_number_8192 + "=" + v_number_8 + "*" + v_number_1024 + ","); 875 | AddToResult(v_number_16384 + "=" + v_number_32 + "*" + v_number_512 + ","); 876 | AddToResult(v_number_32768 + "=" + v_number_4 + "*" + v_number_8192 + ","); 877 | 878 | // "acCdefghilmnoprsStuv. " <- we need these characters 879 | 880 | // "false" = !1+[] 881 | const v_string_false = GetVariableName(); 882 | AddToResult(v_string_false + "=!" + v_number_1 + "+[],"); 883 | 884 | // f = "false"[0] 885 | const v_char_f = GetVariableName(); 886 | AddToResult(v_char_f + "=" + v_string_false + "[" + v_number_0 + "],"); 887 | 888 | // "undefined" = "f"[1]+[] 889 | const v_string_undefined = GetVariableName(); 890 | AddToResult(v_string_undefined + "=" + v_char_f + "[" + v_number_1 + "]+[],"); 891 | 892 | // a = "false"[1] 893 | const v_char_a = GetVariableName(); 894 | AddToResult(v_char_a + "=" + v_string_false + "[" + v_number_1 + "],"); 895 | 896 | // l = "false"[2] 897 | const v_char_l = GetVariableName(); 898 | AddToResult(v_char_l + "=" + v_string_false + "[" + v_number_2 + "],"); 899 | 900 | // s = "false"[3] 901 | const v_char_s = GetVariableName(); 902 | AddToResult(v_char_s + "=" + v_string_false + "[" + v_number_2 + "+" + v_number_1 + "],"); 903 | 904 | // e = "false"[4] 905 | const v_char_e = GetVariableName(); 906 | AddToResult(v_char_e + "=" + v_string_false + "[" + v_number_4 + "],"); 907 | 908 | // i = "undefined"[5] 909 | const v_char_i = GetVariableName(); 910 | AddToResult(v_char_i + "=" + v_string_undefined + "[" + v_number_4 + "+" + v_number_1 + "],"); 911 | 912 | // "[object Object]" 913 | const v_string_object_object = GetVariableName(); 914 | AddToResult(v_string_object_object + "=[]+{},"); 915 | 916 | // c = "[object Object]"[5] 917 | const v_char_c = GetVariableName(); 918 | AddToResult(v_char_c + "=" + v_string_object_object + "[" + v_number_4 + "+" + v_number_1 + "],"); 919 | 920 | // b = "[object Object]"[2] 921 | const v_char_b = GetVariableName(); 922 | AddToResult(v_char_b + "=" + v_string_object_object + "[" + v_number_2 + "],"); 923 | 924 | // u = "undefined"[0] 925 | const v_char_u = GetVariableName(); 926 | AddToResult(v_char_u + "=" + v_string_undefined + "[" + v_number_0 + "],"); 927 | 928 | // n = "undefined"[1] 929 | const v_char_n = GetVariableName(); 930 | AddToResult(v_char_n + "=" + v_string_undefined + "[" + v_number_1 + "],"); 931 | 932 | // t = (!0+[])[0] 933 | const v_char_t = GetVariableName(); 934 | AddToResult(v_char_t + "=(!" + v_number_0 + "+[])[" + v_number_0 + "],"); 935 | 936 | // o = "[object Object]"[1] 937 | const v_char_o = GetVariableName(); 938 | AddToResult(v_char_o + "=" + v_string_object_object + "[" + v_number_1 + "],"); 939 | 940 | // space = "[object Object]"[7] 941 | const v_char_space = GetVariableName(); 942 | AddToResult(v_char_space + "=" + v_string_object_object + "[" + v_number_8 + "-" + v_number_1 + "],"); 943 | 944 | // d = "undefined"[2] 945 | const v_char_d = GetVariableName(); 946 | AddToResult(v_char_d + "=" + v_string_undefined + "[" + v_number_2 + "],"); 947 | 948 | // r = (!0+[])[1] 949 | const v_char_r = GetVariableName(); 950 | AddToResult(v_char_r + "=(!" + v_number_0 + "+[])[" + v_number_1 + "],"); 951 | 952 | // "constructor" 953 | const v_string_constructor = GetVariableName(); 954 | AddToResult( 955 | v_string_constructor + "=" + 956 | [v_char_c, v_char_o, v_char_n, v_char_s, v_char_t, v_char_r, v_char_u, v_char_c, v_char_t, v_char_o, v_char_r,].join("+") 957 | + "," 958 | ); 959 | 960 | // []["filter"] 961 | const v_function_filter = GetVariableName(); 962 | AddToResult(v_function_filter + "=[][" + [v_char_f, v_char_i, v_char_l, v_char_t, v_char_e, v_char_r].join("+") + "],"); 963 | 964 | // []["filter"]["constructor"] 965 | const v_function_function = GetVariableName(); 966 | AddToResult(v_function_function + "=" + v_function_filter + "[" + v_string_constructor + "],"); 967 | 968 | const v_char_g = GetVariableName(); 969 | const v_char_S = GetVariableName(); 970 | 971 | if (target === "nodejs") 972 | { 973 | // `btoa` is not always available on node.js, so we need to use a different approach 974 | // Here, we convert native functions to strings, which always works in node.js, 975 | // but is not consistent across browsers, because it's not part of the ECMAScript standard 976 | 977 | // ''["constructor"]+[] 978 | const v_string_StringConstructor = GetVariableName(); 979 | AddToResult(v_string_StringConstructor + "=''[" + v_string_constructor + "]+[],"); 980 | 981 | // S = (''["constructor"]+[])[1+8] 982 | AddToResult(v_char_S + "=" + v_string_StringConstructor + "[" + v_number_1 + "+" + v_number_8 + "],"); 983 | 984 | // g = (''["constructor"]+[])[16-2] 985 | AddToResult(v_char_g + "=" + v_string_StringConstructor + "[" + v_number_16 + "-" + v_number_2 + "],"); 986 | } 987 | else 988 | { 989 | // []["filter"]["constructor"]("return btoa")() 990 | const v_function_btoa = GetVariableName(); 991 | AddToResult( 992 | v_function_btoa + "=" + v_function_function + "(" + 993 | [v_char_r, v_char_e, v_char_t, v_char_u, v_char_r, v_char_n, v_char_space, v_char_b, v_char_t, v_char_o, v_char_a].join("+") 994 | + ")()," 995 | ); 996 | 997 | // S = btoa("a ")[1] 998 | AddToResult(v_char_S + "=" + v_function_btoa + "(" + v_char_a + "+" + v_char_space + ")[" + v_number_1 + "],"); 999 | 1000 | // g = btoa("b")[1] 1001 | AddToResult(v_char_g + "=" + v_function_btoa + "(" + v_char_b + ")[" + v_number_1 + "],"); 1002 | } 1003 | 1004 | // "toString" 1005 | const v_string_toString = GetVariableName(); 1006 | AddToResult(v_string_toString + "=" + [v_char_t, v_char_o, v_char_S, v_char_t, v_char_r, v_char_i, v_char_n, v_char_g].join("+") + ","); 1007 | 1008 | // p = (16+8+1)["toString"](32) 1009 | const v_char_p = GetVariableName(); 1010 | AddToResult(v_char_p + "=(" + v_number_1 + "+" + v_number_8 + "+" + v_number_16 + ")[" + v_string_toString + "](" + v_number_32 + "),"); 1011 | 1012 | // []["constructor"]["constructor"]("return escape")() 1013 | const v_function_escape = GetVariableName(); 1014 | AddToResult( 1015 | v_function_escape + "=[][" + v_string_constructor + "][" + v_string_constructor + "](" + 1016 | [v_char_r, v_char_e, v_char_t, v_char_u, v_char_r, v_char_n, v_char_space, v_char_e, v_char_s, v_char_c, v_char_a, v_char_p, v_char_e].join("+") 1017 | + ")()," 1018 | ); 1019 | 1020 | // C = escape(''["big"]())[2] 1021 | const v_char_C = GetVariableName(); 1022 | AddToResult(v_char_C + "=" + v_function_escape + "(''[" + [v_char_b, v_char_i, v_char_g].join("+") + "]())[" + v_number_2 + "],"); 1023 | 1024 | // m = (16+4+2)["toString"](32) 1025 | const v_char_m = GetVariableName(); 1026 | AddToResult(v_char_m + "=(" + v_number_16 + "+" + v_number_4 + "+" + v_number_2 + ")[" + v_string_toString + "](" + v_number_32 + "),"); 1027 | 1028 | // h = (16+1)["toString"](32) 1029 | const v_char_h = GetVariableName(); 1030 | AddToResult(v_char_h + "=(" + v_number_16 + "+" + v_number_1 + ")[" + v_string_toString + "](" + v_number_32 + "),"); 1031 | 1032 | // h = (32-1)["toString"](32) 1033 | const v_char_v = GetVariableName(); 1034 | AddToResult(v_char_v + "=(" + v_number_32 + "-" + v_number_1 + ")[" + v_string_toString + "](" + v_number_32 + "),"); 1035 | 1036 | // ''["constructor"]["fromCharCode"] 1037 | const v_function_stringFromCharCode = GetVariableName(); 1038 | AddToResult( 1039 | v_function_stringFromCharCode + "=''[" + v_string_constructor + "][" + 1040 | [v_char_f, v_char_r, v_char_o, v_char_m, v_char_C, v_char_h, v_char_a, v_char_r, v_char_C, v_char_o, v_char_d, v_char_e].join("+") 1041 | + "]," 1042 | ); 1043 | 1044 | // []["filter"]["constructor"]("return eval")() 1045 | const v_function_eval = GetVariableName(); 1046 | AddToResult( 1047 | v_function_eval + "=" + v_function_function + "(" + 1048 | [v_char_r, v_char_e, v_char_t, v_char_u, v_char_r, v_char_n, v_char_space, v_char_e, v_char_v, v_char_a, v_char_l].join("+") 1049 | + ")()," 1050 | ); 1051 | 1052 | let v_bool_deobfuscationOk = ""; 1053 | if (deobfuscationProtection) 1054 | { 1055 | const dummyString = GetRandomStr(allowLongerName); 1056 | const v_string_dummyString = GetVariableName(); 1057 | AddToResult(v_string_dummyString + "='" + dummyString + "',"); 1058 | 1059 | const v_function_deobfuscationProtection = GetVariableName(); 1060 | const functionStart = target === "es5-" ? "function()" : "()=>"; 1061 | AddToResult(v_function_deobfuscationProtection + "=" + functionStart + "{'" + dummyString + "'},"); 1062 | 1063 | // How this works: 1064 | // In javascript, you can convert functions to a string, and if you convert a user-defined function to string, 1065 | // you'll get back the exact string representation of the function 1066 | // Deobfuscators usually format the code to make it more readable, so we create a dummy function which will be formatted for sure 1067 | // So we store the original string representation, and compare it with the new one 1068 | // If the code was formatted, then those two won't match, and we can do stuff with that 1069 | 1070 | v_bool_deobfuscationOk = GetVariableName(); 1071 | AddToResult( 1072 | v_bool_deobfuscationOk + "=(" + v_function_deobfuscationProtection + "+[])[" 1073 | + [v_char_s, v_char_u, v_char_b, v_char_s, v_char_t, v_char_r].join("+") 1074 | + "](" + GetNumber(functionStart.length + 2) + "," + GetNumber(dummyString.length) + ")==" + v_string_dummyString + "," 1075 | ); 1076 | 1077 | if (deobfuscationProtection.type !== "skip") 1078 | { 1079 | const v_string_otherFunctionName = GetVariableName(); 1080 | const v_string_param1_deobfuscationProtection = GetVariableName(); 1081 | 1082 | AddToResult(v_string_otherFunctionName + "=" + v_function_function + "('" + v_string_param1_deobfuscationProtection + "',"); 1083 | 1084 | function GetCharCodesOf(str: string) 1085 | { 1086 | const charCodes = GetCharCodes(str); 1087 | return v_function_stringFromCharCode + "(" + charCodes.map(charCode => GetNumber(charCode)) 1088 | .join(",") + ")"; 1089 | } 1090 | 1091 | switch (deobfuscationProtection.type) 1092 | { 1093 | case "error": 1094 | { 1095 | AddToResult(GetCharCodesOf("!function \u0192(){" + v_string_param1_deobfuscationProtection + "=='" + dummyString + "'||\u0192()}()")); 1096 | break; 1097 | } 1098 | case "loop": 1099 | { 1100 | AddToResult(GetCharCodesOf("if(" + v_string_param1_deobfuscationProtection + "!='" + dummyString + "')for(;;){}")); 1101 | break; 1102 | } 1103 | case "custom": 1104 | { 1105 | AddToResult(GetCharCodesOf( 1106 | v_string_param1_deobfuscationProtection + "=='" + dummyString 1107 | + "'||!function(){" + deobfuscationProtection.codeToRun + "}()" 1108 | )); 1109 | break; 1110 | } 1111 | default: 1112 | Unreachable(deobfuscationProtection); 1113 | } 1114 | 1115 | AddToResult( 1116 | ")((" + v_function_deobfuscationProtection + "+[])[" 1117 | + [v_char_s, v_char_u, v_char_b, v_char_s, v_char_t, v_char_r].join("+") 1118 | + "](" + GetNumber(functionStart.length + 2) + "," + GetNumber(dummyString.length) + "))," 1119 | ); 1120 | } 1121 | } 1122 | 1123 | const finalLetters: string[] = []; 1124 | for (const [_, letterData] of letters) 1125 | { 1126 | finalLetters.push(letterData.assignmentExpr); 1127 | } 1128 | 1129 | Shuffle(finalLetters); 1130 | 1131 | AddToResult(finalLetters.join(",") + ";"); 1132 | 1133 | if (deobfuscationProtection) 1134 | { 1135 | AddToResult(v_bool_deobfuscationOk + "&&"); 1136 | } 1137 | 1138 | AddToResult(v_function_eval + "("); 1139 | 1140 | // Assemble the characters from the input code, using String.fromCharCode 1141 | // The maximum number of function arguments is limited (e.g. 65535 on chrome), so for long texts, we need to split up the process 1142 | // `batchCount` indicates how many arguments we use for a function call 1143 | // 4096 should be fine on all browsers 1144 | // Also add random count at a time 1145 | const batchCount = 4096; 1146 | const batchData: string[] = []; 1147 | let textIndex = 0; 1148 | while (textIndex < inputCharCodes.length) 1149 | { 1150 | const finalTextAssembled = []; 1151 | const batchEndIndex = Math.min(textIndex + Math.floor(batchCount * (Random() * 0.5 + 0.5)), inputCharCodes.length); 1152 | 1153 | while (textIndex < batchEndIndex) 1154 | { 1155 | const letterData = letters.get(inputCharCodes[textIndex])!; 1156 | finalTextAssembled.push(letterData.variableName); 1157 | ++textIndex; 1158 | } 1159 | 1160 | const currentBatchText = v_function_stringFromCharCode + "(" + finalTextAssembled.join(",") + ")"; 1161 | batchData.push(currentBatchText); 1162 | } 1163 | 1164 | AddToResult(batchData.join("+")); 1165 | AddToResult(")"); 1166 | AddToResult("})()"); 1167 | 1168 | return resultSegments.join(""); 1169 | } 1170 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import { Charset, CharsetType, CheckCustomCharacters, CodeGenerationTarget, DeobfuscationProtection, DeobfuscationProtectionType, Obfuscate, ObfuscateError } from "./lib.js"; 2 | 3 | document.title = "j̏ͭͯͤͫ͗͋s͔̣͎̎̑ͭͩ-̣͇ͮͦZ̹͚̫̪͓̠͐ͭ͊̽̾̽ ͍̣̯ͮ̏̂̄ͧͯ̚-͗̍̂ͯ̅̚ ͬ̂ͯ͋J̞̩̠͓͈̳̩ã̰̥̞͚͓v͚̞̯̺͇ḁ̲̹̤͎ͤS̱̰̠̣͚͛̈̌̄̚ͅc͖̥͉̹̮̻̓̊̑ri̭̗̳̩̾p͖͇̟͒t̅̇͋ͅ ̥o̯̗̱b͖̦̹̣̞f̺̥̽̂u͎̖̦̻̻͔̗̐ͥ̂̓̒̅̅s̰̪̞ͫ̿c̲͈̪͓̖ͭ̽â̩͙ͅt̻̺͚ō̝̗͔̗̰r̠̩̤͚̋ͩ"; 4 | 5 | const DOM = { 6 | mainPage: document.getElementById("main_page")!, 7 | codeInputTextArea: document.getElementById("text_area") as HTMLTextAreaElement, 8 | obfuscateResultTextArea: document.getElementById("result_text") as HTMLTextAreaElement, 9 | zalgoLevelSliderContainer: document.getElementById("zalgo_level_slider_container")!, 10 | zalgoLevelText: document.getElementById("zalgo_level_text")!, 11 | zalgoLevelSlider: document.getElementById("zalgo_level_slider") as HTMLInputElement, 12 | variableNameLengthSlider: document.getElementById("variable_name_length_slider") as HTMLInputElement, 13 | variableNameLengthText: document.getElementById("variable_name_length_text")!, 14 | charsetSelector: document.getElementById("charset_selector") as HTMLSelectElement, 15 | zalgoCharsetOption: document.getElementById("type_zalgo") as HTMLOptionElement, 16 | invisibleCharsetOption: document.getElementById("type_invisible") as HTMLOptionElement, 17 | optionsOverlay: document.getElementById("options_overlay")!, 18 | deobfuscationProtectionOptions: document.getElementById("deobfuscation_protection_options")!, 19 | customizeCharactersButton: document.getElementById("custom_charset_customizer_button") as HTMLButtonElement, 20 | customizeCharactersPage: document.getElementById("customize_page")!, 21 | customizeCharactersTextArea: document.getElementById("customize_text_area") as HTMLTextAreaElement, 22 | customCodeEditButton: document.getElementById("custom_code_edit_button") as HTMLButtonElement, 23 | customCodeEditPage: document.getElementById("customize_deobfuscation_code_page")!, 24 | customCodeEditTextArea: document.getElementById("deobfuscation_custom_code_text_area") as HTMLTextAreaElement 25 | }; 26 | 27 | function CopyResult(button: HTMLButtonElement) 28 | { 29 | button.innerText = "Copied!"; 30 | navigator.clipboard.writeText(DOM.obfuscateResultTextArea.value); 31 | } 32 | 33 | function LogarithmicSliderMapValue(sliderMin: number, sliderMax: number, base: number, rawValue: number) 34 | { 35 | const value = (Math.pow(base, rawValue) - 1) / (base - 1); 36 | return Math.round(sliderMin + (sliderMax - sliderMin) * value); 37 | } 38 | 39 | function LogarithmicSliderUnmapValue(sliderMin: number, sliderMax: number, base: number, targetValue: number) 40 | { 41 | const t = (targetValue - sliderMin) / (sliderMax - sliderMin); 42 | const x = t * (base - 1) + 1; 43 | return Math.log(x) / Math.log(base); 44 | } 45 | 46 | const zalgoLevelSliderMinValue = 0; 47 | const zalgoLevelSliderMaxValue = 100; 48 | const zalgoLevelSliderBase = 10; 49 | 50 | let zalgoLevel = 3; 51 | function slider_zalgoLevel(e: HTMLInputElement) 52 | { 53 | zalgoLevel = LogarithmicSliderMapValue( 54 | zalgoLevelSliderMinValue, 55 | zalgoLevelSliderMaxValue, 56 | zalgoLevelSliderBase, 57 | Number(e.value) 58 | ); 59 | 60 | DOM.zalgoLevelText.textContent = "Zalgo level: " + zalgoLevel.toString(); 61 | } 62 | 63 | DOM.zalgoLevelSlider.value = LogarithmicSliderUnmapValue( 64 | zalgoLevelSliderMinValue, 65 | zalgoLevelSliderMaxValue, 66 | zalgoLevelSliderBase, 67 | zalgoLevel 68 | ).toString(); 69 | 70 | const variableNameLengthSliderMinValue = 2; 71 | const variableNameLengthSliderMaxValue = 50; 72 | const variableNameLengthSliderBase = 10; 73 | 74 | let variableNameLength = 5; 75 | function slider_variableLength(e: HTMLInputElement) 76 | { 77 | variableNameLength = LogarithmicSliderMapValue( 78 | variableNameLengthSliderMinValue, 79 | variableNameLengthSliderMaxValue, 80 | variableNameLengthSliderBase, 81 | Number(e.value) 82 | ); 83 | 84 | DOM.variableNameLengthText.textContent = "Variable name length: " + variableNameLength.toString(); 85 | } 86 | 87 | DOM.variableNameLengthSlider.value = LogarithmicSliderUnmapValue( 88 | variableNameLengthSliderMinValue, 89 | variableNameLengthSliderMaxValue, 90 | variableNameLengthSliderBase, 91 | variableNameLength 92 | ).toString(); 93 | 94 | function ShowOptions(show: boolean) 95 | { 96 | DOM.optionsOverlay.style.display = show ? "flex" : "none"; 97 | DOM.mainPage.style.filter = show ? "blur(3px)" : ""; 98 | } 99 | 100 | function ToggleCustomizeMode(on: boolean) 101 | { 102 | if (!on && !ValidateCustomCharacters()) 103 | { 104 | return; 105 | } 106 | 107 | DOM.mainPage.style.display = on ? "none" : ""; 108 | DOM.customizeCharactersPage.style.display = on ? "" : "none"; 109 | } 110 | 111 | let customCharacters: string[] = []; 112 | function ValidateCustomCharacters() 113 | { 114 | const characters = [...DOM.customizeCharactersTextArea.value]; 115 | 116 | const { startingChars, nonStartingChars, invalidChars } = CheckCustomCharacters(characters); 117 | 118 | const invalidCharsDiv = document.getElementById("invalid_chars")!; 119 | if (invalidChars.size !== 0) 120 | { 121 | const codePointsDivText = Array.from(invalidChars).reduce((acc, curr) => 122 | { 123 | const codePoint = curr.codePointAt(0); 124 | if (codePoint === undefined) 125 | { 126 | return acc; 127 | } 128 | 129 | if (curr === " ") 130 | { 131 | curr = "space"; 132 | } 133 | 134 | return acc + "
U+" 135 | + codePoint.toString(16).toUpperCase().padStart(4, "0") + " " + curr + "
"; 136 | }, ""); 137 | 138 | invalidCharsDiv.innerHTML = "
The following character" + (invalidChars.size === 1 ? "" : "s") 139 | + " cannot be used in variable names:
" + codePointsDivText; 140 | 141 | invalidCharsDiv.style.display = ""; 142 | return false; 143 | } 144 | else if (startingChars.length === 0 && nonStartingChars.length !== 0) 145 | { 146 | invalidCharsDiv.innerHTML = "
There are no characters that are valid starting characters in variable names
"; 147 | invalidCharsDiv.style.display = ""; 148 | return false; 149 | } 150 | else 151 | { 152 | invalidCharsDiv.style.display = "none"; 153 | customCharacters = characters; 154 | return true; 155 | } 156 | } 157 | 158 | let charset: CharsetType = "zalgo"; 159 | function CharsetChanged(name: string) 160 | { 161 | switch (name) 162 | { 163 | case "zalgo": 164 | case "invisible": 165 | case "iiii": 166 | case "lines": 167 | case "custom": 168 | charset = name; 169 | break; 170 | default: 171 | return; 172 | } 173 | 174 | DOM.zalgoLevelSliderContainer.style.display = (charset === "zalgo") ? "" : "none"; 175 | DOM.customizeCharactersButton.style.display = (charset === "custom") ? "" : "none"; 176 | } 177 | 178 | let codeGenerationTarget: CodeGenerationTarget = "es6+"; 179 | function CodeGenerationTargetChanged(target: string) 180 | { 181 | switch (target) 182 | { 183 | case "es5-": 184 | case "es6+": 185 | case "nodejs": 186 | codeGenerationTarget = target; 187 | break; 188 | default: 189 | return; 190 | } 191 | 192 | const newFeaturesDisabled = codeGenerationTarget === "es5-"; 193 | 194 | DOM.zalgoCharsetOption.disabled = newFeaturesDisabled; 195 | DOM.invisibleCharsetOption.disabled = newFeaturesDisabled; 196 | 197 | if (newFeaturesDisabled) 198 | { 199 | const selectedValue = DOM.charsetSelector.value; 200 | if (selectedValue === "zalgo" || selectedValue === "invisible") 201 | { 202 | DOM.charsetSelector.value = "iiii"; 203 | CharsetChanged("iiii"); 204 | } 205 | } 206 | } 207 | 208 | let isDeobfuscationProtection = false; 209 | let deobfuscationProtectionMode: DeobfuscationProtectionType = "skip"; 210 | 211 | function DeobfuscationProtectionChanged(on: boolean) 212 | { 213 | isDeobfuscationProtection = on; 214 | DOM.deobfuscationProtectionOptions.style.visibility = on ? "" : "hidden"; 215 | DOM.customCodeEditButton.style.display = (deobfuscationProtectionMode === "custom" && on) ? "" : "none"; 216 | } 217 | 218 | function DeobfuscationProtectionModeChanged(mode: string) 219 | { 220 | switch (mode) 221 | { 222 | case "skip": 223 | case "error": 224 | case "loop": 225 | case "custom": 226 | deobfuscationProtectionMode = mode; 227 | break; 228 | default: 229 | return; 230 | } 231 | 232 | DOM.customCodeEditButton.style.display = (mode === "custom" && isDeobfuscationProtection) ? "" : "none"; 233 | } 234 | 235 | function ShowCustomCodeEditor(show: boolean) 236 | { 237 | ShowOptions(false); 238 | DOM.mainPage.style.display = show ? "none" : ""; 239 | DOM.customCodeEditPage.style.display = show ? "" : "none"; 240 | } 241 | 242 | function AutoResizeTextarea(textarea: HTMLTextAreaElement, padding: number) 243 | { 244 | textarea.style.height = "auto"; 245 | textarea.style.height = (textarea.scrollHeight - padding * 2) + "px"; 246 | } 247 | 248 | function ObfuscateButton() 249 | { 250 | const text = DOM.codeInputTextArea.value; 251 | 252 | const charsetData = ((): Charset => 253 | { 254 | switch (charset) 255 | { 256 | case "zalgo": return { 257 | type: "zalgo", 258 | zalgoLevel 259 | }; 260 | case "custom": return { 261 | type: "custom", 262 | characters: customCharacters 263 | }; 264 | default: return { 265 | type: charset 266 | }; 267 | } 268 | })(); 269 | 270 | const deobfuscationProtection = ((): DeobfuscationProtection | undefined => 271 | { 272 | if (!isDeobfuscationProtection) 273 | { 274 | return undefined; 275 | } 276 | 277 | switch (deobfuscationProtectionMode) 278 | { 279 | case "custom": return { 280 | type: "custom", 281 | codeToRun: DOM.customCodeEditTextArea.value 282 | }; 283 | default: return { 284 | type: deobfuscationProtectionMode 285 | }; 286 | } 287 | })(); 288 | 289 | let result = ""; 290 | try 291 | { 292 | result = Obfuscate(text, { 293 | charset: charsetData, 294 | variableNameLength, 295 | target: codeGenerationTarget, 296 | deobfuscationProtection 297 | }); 298 | } 299 | catch (ex: unknown) 300 | { 301 | if (ex instanceof ObfuscateError) 302 | { 303 | alert(ex.message); 304 | } 305 | 306 | return; 307 | } 308 | 309 | DOM.obfuscateResultTextArea.value = result; 310 | } 311 | 312 | // Set functions on the window object, so that they are callable from html 313 | const windowAny = window as any; 314 | windowAny.CopyResult = CopyResult; 315 | windowAny.slider_zalgoLevel = slider_zalgoLevel; 316 | windowAny.slider_variableLength = slider_variableLength; 317 | windowAny.ShowOptions = ShowOptions; 318 | windowAny.ToggleCustomizeMode = ToggleCustomizeMode; 319 | windowAny.CharsetChanged = CharsetChanged; 320 | windowAny.CodeGenerationTargetChanged = CodeGenerationTargetChanged; 321 | windowAny.DeobfuscationProtectionChanged = DeobfuscationProtectionChanged; 322 | windowAny.DeobfuscationProtectionModeChanged = DeobfuscationProtectionModeChanged; 323 | windowAny.ShowCustomCodeEditor = ShowCustomCodeEditor; 324 | windowAny.AutoResizeTextarea = AutoResizeTextarea; 325 | windowAny.ObfuscateButton = ObfuscateButton; 326 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 6 | "module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | // "lib": [], /* Specify library files to be included in the compilation. */ 8 | // "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // "checkJs": true, /* Report errors in .js files. */ 10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 14 | // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | "outDir": "./dist/", /* Redirect output structure to the directory. */ 16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 17 | // "composite": true, /* Enable project compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | 53 | /* Source Map Options */ 54 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 55 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 56 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 57 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 58 | 59 | /* Experimental Options */ 60 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 61 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 62 | 63 | /* Advanced Options */ 64 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 65 | } 66 | } 67 | --------------------------------------------------------------------------------