Back in Function Main Again. Github
JavaScript obfuscator
JavaScript Obfuscator is a powerful costless obfuscator for JavaScript, containing a variety of features which provide protection for your source lawmaking.
Key features:
- variables renaming
- strings extraction and encryption
- expressionless code injection
- control flow flattening
- various code transformations
- and more...
The example of obfuscated code: github.com
Online version:
obfuscator.io
Plugins:
- Webpack plugin: webpack-obfuscator
- Webpack loader: obfuscator-loader
- Gulp: gulp-javascript-obfuscator
- Grunt: grunt-contrib-obfuscator
- Rollup: rollup-plugin-javascript-obfuscator
- Weex: weex-devtool
- Malta: malta-js-obfuscator
- Netlify plugin: netlify-plugin-js-obfuscator
Yous can back up this project by donating:
- (Bitcoin) bc1q203p8nyrstwm7vwzjg3h9l9t6y9ka0umw0rx96
Huge thanks to all supporters!
NOTE! the README on the master branch might non lucifer that of the latest stable release!
If you take a question, check this section commencement: FAQ
⚠️ Important
Only obfuscate the code that belongs to yous.
It is not recommended to obfuscate vendor scripts and polyfills, since the obfuscated lawmaking is fifteen-lxxx% slower (depends on options) and the files are significantly larger.
Installation
Using Yarn or NPM
Install the package with Yarn or NPM and add information technology to your dependencies
or devDependencies
:
$ yarn add --dev javascript-obfuscator
or
$ npm install --save-dev javascript-obfuscator
In a Browser
From CDN:
< script src="https://cdn.jsdelivr.internet/npm/javascript-obfuscator/dist/index.browser.js"> </ script >
From node_modules
:
< script src="./node_modules/javascript-obfuscator/dist/index.browser.js"> </ script >
Usage
var JavaScriptObfuscator = require ( 'javascript-obfuscator' ) ; var obfuscationResult = JavaScriptObfuscator . obfuscate ( ` (function(){ var variable1 = '5' - 3; var variable2 = 'five' + 3; var variable3 = '5' + - '2'; var variable4 = ['x','10','ten','ten','10'].map(parseInt); var variable5 = 'foo ' + i + 1; console.log(variable1); console.log(variable2); panel.log(variable3); console.log(variable4); console.log(variable5); })(); ` , { compact: false , controlFlowFlattening: true , controlFlowFlatteningThreshold: ane , numbersToExpressions: true , simplify: true , stringArrayShuffle: truthful , splitStrings: true , stringArrayThreshold: i } ) ; panel . log ( obfuscationResult . getObfuscatedCode ( ) ) ; /* var _0x9947 = [ 'map', 'log', 'foo\x20', 'bvmqO', '133039ViRMWR', 'xPfLC', 'ytpdx', '1243717qSZCyh', '2|seven|4|6|9|', '1ErtbCr', '1608314VKvthn', '1ZRaFKN', 'XBoAA', '423266kQOYHV', 'iii|0|five|eight|1', '235064xPNdKe', '13RUDZfG', '157gNPQGm', '1639212MvnHZL', 'rDjOa', 'iBHph', '9926iRHoRl', 'separate' ]; function _0x33e4(_0x1809b5, _0x37ef6e) { return _0x33e4 = office (_0x338a69, _0x39ad79) { _0x338a69 = _0x338a69 - (0x1939 + -0xf * 0x1f3 + 0x1 * 0x469); var _0x2b223a = _0x9947[_0x338a69]; return _0x2b223a; }, _0x33e4(_0x1809b5, _0x37ef6e); } (part (_0x431d87, _0x156c7f) { var _0x10cf6e = _0x33e4; while (!![]) { try { var _0x330ad1 = -parseInt(_0x10cf6e(0x6c)) * -parseInt(_0x10cf6e(0x6d)) + -parseInt(_0x10cf6e(0x74)) * -parseInt(_0x10cf6e(0x78)) + parseInt(_0x10cf6e(0x6a)) + -parseInt(_0x10cf6e(0x70)) + parseInt(_0x10cf6e(0x6e)) * -parseInt(_0x10cf6e(0x75)) + parseInt(_0x10cf6e(0x72)) + -parseInt(_0x10cf6e(0x67)) * parseInt(_0x10cf6e(0x73)); if (_0x330ad1 === _0x156c7f) break; else _0x431d87['push'](_0x431d87['shift']()); } grab (_0x9f878) { _0x431d87['push'](_0x431d87['shift']()); } } }(_0x9947, -0xb6270 + 0x4dfd2 * 0x2 + 0x75460 * 0x2), role () { var _0x1f346d = _0x33e4, _0x860db8 = { 'ytpdx': _0x1f346d(0x6b) + _0x1f346d(0x71), 'bvmqO': office (_0x560787, _0x519b9e) { return _0x560787 - _0x519b9e; }, 'rDjOa': office (_0x4501fe, _0x2b07a3) { return _0x4501fe + _0x2b07a3; }, 'xPfLC': function (_0x5f3c9b, _0x434936) { return _0x5f3c9b + _0x434936; }, 'XBoAA': role (_0x535b8a, _0x42eef4) { return _0x535b8a + _0x42eef4; }, 'iBHph': _0x1f346d(0x65) }, _0x346c55 = _0x860db8[_0x1f346d(0x69)][_0x1f346d(0x79)]('|'), _0x3bf817 = 0x4bb * 0x1 + 0x801 + -0xcbc; while (!![]) { switch (_0x346c55[_0x3bf817++]) { case '0': console[_0x1f346d(0x7b)](_0x4c96d8); go along; instance 'i': console[_0x1f346d(0x7b)](_0x101028); continue; case '2': var _0x65977d = _0x860db8[_0x1f346d(0x66)]('v', -0x586 + -0x2195 + -0x6 * -0x685); keep; case 'iii': panel[_0x1f346d(0x7b)](_0x65977d); keep; case '4': var _0x56d39b = _0x860db8[_0x1f346d(0x76)]('v', -'2'); continue; instance '5': console[_0x1f346d(0x7b)](_0x56d39b); go along; case '6': var _0x544285 = [ '10', '10', 'ten', '10', '10' ][_0x1f346d(0x7a)](parseInt); continue; case 'vii': var _0x4c96d8 = _0x860db8[_0x1f346d(0x68)]('5', 0x622 * -0x6 + 0x4a * 0x3 + 0x1 * 0x23f1); go along; case 'eight': console[_0x1f346d(0x7b)](_0x544285); go along; case '9': var _0x101028 = _0x860db8[_0x1f346d(0x6f)](_0x860db8[_0x1f346d(0x6f)](_0x860db8[_0x1f346d(0x77)], 0x6fb * 0x5 + 0x1ebf * 0x1 + -0x41a5), 0x209 * 0xa + 0x1314 + -0x276d); proceed; } intermission; } }()); */
obfuscate(sourceCode, options)
Returns ObfuscationResult
object which contains 2 public methods:
-
getObfuscatedCode()
- returnsstring
with obfuscated code; -
getSourceMap()
- ifsourceMap
option is enabled - returnsstring
with source map or an empty string ifsourceMapMode
choice is set every bitinline
; -
getIdentifierNamesCache()
- returns object with identifier names cache ifidentifierNamesCache
option is enabled,null
overwise.
Calling toString()
for ObfuscationResult
object volition return string
with obfuscated code.
Method takes 2 parameters, sourceCode
and options
– the source lawmaking and the options respectively:
-
sourceCode
(string
, default:null
) – any valid source lawmaking, passed as a cord; -
options
(Object
, default:null
) – an object with options.
For available options, see options.
obfuscateMultiple(sourceCodesObject, options)
Accepts sourceCodesObject
that is a map which keys are identifiers of source codes and values are source codes:
{ foo: 'var foo = i;', bar: 'var bar = 2;' }
Returns a map object which keys are identifiers of source codes and values are ObfuscationResult
objects.
getOptionsByPreset(optionsPreset)
Returns an options object for the passed options preset name.
CLI usage
Come across CLI options.
Obfuscate unmarried file
Usage:
javascript-obfuscator input_file_name.js [options] javascript-obfuscator input_file_name.js --output output_file_name.js [options] javascript-obfuscator input_file_name.js --output output_folder_name [options] javascript-obfuscator input_folder_name --output output_folder_name [options]
Obfuscation of single input file with .js
extension.
If the destination path is not specified with the --output
option, the obfuscated file volition exist saved into the input file directory, with INPUT_FILE_NAME-obfuscated.js
name.
Some examples:
javascript-obfuscator samples/sample.js --compact true --self-defending simulated // creates a new file samples/sample-obfuscated.js javascript-obfuscator samples/sample.js --output output/output.js --compact truthful --self-defending false // creates a new file output/output.js
Obfuscate directory recursively
Usage:
javascript-obfuscator ./dist [options] // creates a new obfuscated files under `./dist` directory most the input files with `obfuscated` postfix javascript-obfuscator ./dist --output ./dist/obfuscated [options] // creates a binder structure with obfuscated files under `./dist/obfuscated` path
Obfuscation of all .js
files under input directory. If this directory contains already obfuscated files with -obfuscated
postfix - these files will ignored.
Obfuscated files will saved into the input directory under INPUT_FILE_NAME-obfuscated.js
name.
Provisional comments
You lot can disable and enable obfuscation for specific parts of the code by adding following comments:
- disable:
// javascript-obfuscator:disable
or/* javascript-obfuscator:disable */
; - enable:
// javascript-obfuscator:enable
or/* javascript-obfuscator:enable */
.
Example:
// input var foo = one ; // javascript-obfuscator:disable var bar = 2 ; // output var _0xabc123 = 0x1 ; var bar = 2 ;
Conditional comments touch on only directly transformations of AST-tree nodes. All child transformations yet will exist applied to the AST-tree nodes.
For example:
- Obfuscation of the variable'due south name at its announcement is called direct transformation;
- Obfuscation of the variable'south name beyond its proclamation is chosen kid transformation.
Kind of variables
Kind of variables of inserted nodes will automobile-detected, based on most prevailing kind of variables of source lawmaking.
Conflicts of identifier names between different files
During obfuscation of the different files, the same names can be generated for the global identifiers between these files. To preclude this set the unique prefix for all global identifiers for each obfuscated file with identifiersPrefix
option.
When using CLI this prefix will be added automatically.
JavaScript Obfuscator Options
Following options are bachelor for the JS Obfuscator:
options:
{ meaty: true , controlFlowFlattening: simulated , controlFlowFlatteningThreshold: 0.75 , deadCodeInjection: fake , deadCodeInjectionThreshold: 0.iv , debugProtection: false , debugProtectionInterval: 0 , disableConsoleOutput: false , domainLock: [ ] , domainLockRedirectUrl: 'nearly:bare' , forceTransformStrings: [ ] , identifierNamesCache: null , identifierNamesGenerator: 'hexadecimal' , identifiersDictionary: [ ] , identifiersPrefix: '' , ignoreRequireImports: faux , inputFileName: '' , log: fake , numbersToExpressions: false , optionsPreset: 'default' , renameGlobals: fake , renameProperties: false , renamePropertiesMode: 'safe' , reservedNames: [ ] , reservedStrings: [ ] , seed: 0 , selfDefending: false , simplify: truthful , sourceMap: simulated , sourceMapBaseUrl: '' , sourceMapFileName: '' , sourceMapMode: 'separate' , sourceMapSourcesMode: 'sources-content' , splitStrings: false , splitStringsChunkLength: 10 , stringArray: true , stringArrayCallsTransform: true , stringArrayCallsTransformThreshold: 0.5 , stringArrayEncoding: [ ] , stringArrayIndexesType: [ 'hexadecimal-number' ] , stringArrayIndexShift: true , stringArrayRotate: true , stringArrayShuffle: true , stringArrayWrappersCount: 1 , stringArrayWrappersChainedCalls: true , stringArrayWrappersParametersMaxCount: ii , stringArrayWrappersType: 'variable' , stringArrayThreshold: 0.75 , target: 'browser' , transformObjectKeys: imitation , unicodeEscapeSequence: false }
CLI options:
-v, --version -h, --help -o, --output --compact <boolean> --config <string> --control-catamenia-flattening <boolean> --command-flow-flattening-threshold <number> --expressionless-lawmaking-injection <boolean> --dead-code-injection-threshold <number> --debug-protection <boolean> --debug-protection-interval <number> --disable-console-output <boolean> --domain-lock '<listing>' (comma separated) --domain-lock-redirect-url <string> --exclude '<list>' (comma separated) --force-transform-strings '<list>' (comma separated) --identifier-names-cache-path <string> --identifier-names-generator <string> [dictionary, hexadecimal, mangled, mangled-shuffled] --identifiers-dictionary '<list>' (comma separated) --identifiers-prefix <string> --ignore-imports <boolean> --log <boolean> --numbers-to-expressions <boolean> --options-preset <string> [default, low-obfuscation, medium-obfuscation, high-obfuscation] --rename-globals <boolean> --rename-properties <boolean> --rename-backdrop-style <string> [safe, dangerous] --reserved-names '<listing>' (comma separated) --reserved-strings '<list>' (comma separated) --seed <cord|number> --cocky-defending <boolean> --simplify <boolean> --source-map <boolean> --source-map-base-url <string> --source-map-file-name <string> --source-map-mode <string> [inline, split up] --source-map-sources-mode <string> [sources, sources-content] --split-strings <boolean> --split-strings-chunk-length <number> --string-array <boolean> --string-array-calls-transform <boolean> --string-array-calls-transform-threshold <number> --string-array-encoding '<list>' (comma separated) [none, base64, rc4] --cord-array-indexes-blazon '<list>' (comma separated) [hexadecimal-number, hexadecimal-numeric-cord] --string-assortment-index-shift <boolean> --string-array-rotate <boolean> --cord-assortment-shuffle <boolean> --cord-array-wrappers-count <number> --string-assortment-wrappers-chained-calls <boolean> --string-array-wrappers-parameters-max-count <number> --string-assortment-wrappers-type <string> [variable, part] --string-array-threshold <number> --target <string> [browser, browser-no-eval, node] --transform-object-keys <boolean> --unicode-escape-sequence <boolean>
compact
Type: boolean
Default: true
Compact code output on one line.
config
Blazon: string
Default: ``
Name of JS/JSON config file which contains obfuscator options. These will be overridden past options passed direct to CLI
controlFlowFlattening
Type: boolean
Default: faux
⚠️ This choice greatly affects the performance upwardly to one.5x slower runtime speed. Use controlFlowFlatteningThreshold
to gear up percent of nodes that will affected by control menstruum flattening.
Enables code control menstruum flattening. Command flow flattening is a structure transformation of the source code that hinders programme comprehension.
Example:
// input ( role ( ) { office foo ( ) { render function ( ) { var sum = one + 2 ; console . log ( ane ) ; console . log ( 2 ) ; panel . log ( 3 ) ; console . log ( 4 ) ; panel . log ( 5 ) ; console . log ( 6 ) ; } } foo ( ) ( ) ; } ) ( ) ; // output ( function ( ) { function _0x3bfc5c ( ) { render function ( ) { var _0x3260a5 = { 'WtABe': '4|0|6|v|3|2|1' , 'GokKo': function _0xf87260 ( _0x427a8e , _0x43354c ) { return _0x427a8e + _0x43354c ; } } ; var _0x1ad4d6 = _0x3260a5 [ 'WtABe' ] [ 'split' ] ( '|' ) , _0x1a7b12 = 0x0 ; while ( ! ! [ ] ) { switch ( _0x1ad4d6 [ _0x1a7b12 ++ ] ) { case '0': console [ 'log' ] ( 0x1 ) ; continue ; case '1': console [ 'log' ] ( 0x6 ) ; proceed ; case 'ii': console [ 'log' ] ( 0x5 ) ; continue ; case '3': panel [ 'log' ] ( 0x4 ) ; keep ; case '4': var _0x1f2f2f = _0x3260a5 [ 'GokKo' ] ( 0x1 , 0x2 ) ; go on ; case '5': console [ 'log' ] ( 0x3 ) ; continue ; case 'half dozen': console [ 'log' ] ( 0x2 ) ; go along ; } break ; } } ; } _0x3bfc5c ( ) ( ) ; } ( ) ) ;
controlFlowFlatteningThreshold
Type: number
Default: 0.75
Min: 0
Max: 1
The probability that the controlFlowFlattening
transformation will be practical to any given node.
This setting is especially useful for large code size considering big amounts of control flow transformations can slow downwardly your code and increment lawmaking size.
controlFlowFlatteningThreshold: 0
equals to controlFlowFlattening: false
.
deadCodeInjection
Type: boolean
Default: simulated
⚠️ Dramatically increases size of obfuscated code (up to 200%), use just if size of obfuscated code doesn't affair. Use deadCodeInjectionThreshold
to set per centum of nodes that will affected by dead lawmaking injection.
⚠️ This option forcibly enables stringArray
option.
With this choice, random blocks of dead code will be added to the obfuscated lawmaking.
Instance:
// input ( function ( ) { if ( true ) { var foo = function ( ) { console . log ( 'abc' ) ; } ; var bar = function ( ) { panel . log ( 'def' ) ; } ; var baz = function ( ) { console . log ( 'ghi' ) ; } ; var bark = function ( ) { console . log ( 'jkl' ) ; } ; var hawk = function ( ) { console . log ( 'mno' ) ; } ; foo ( ) ; bar ( ) ; baz ( ) ; bawl ( ) ; hawk ( ) ; } } ) ( ) ; // output var _0x37b8 = [ 'YBCtz' , 'GlrkA' , 'urPbb' , 'abc' , 'NMIhC' , 'yZgAj' , 'zrAId' , 'EtyJA' , 'log' , 'mno' , 'jkl' , 'def' , 'Quzya' , 'IWbBa' , 'ghi' ] ; function _0x43a7 ( _0x12cf56 , _0x587376 ) { _0x43a7 = office ( _0x2f87a8 , _0x47eac2 ) { _0x2f87a8 = _0x2f87a8 - ( 0x16a7 * 0x1 + 0x5 * 0x151 + - 0x1c92 ) ; var _0x341e03 = _0x37b8 [ _0x2f87a8 ] ; return _0x341e03 ; } ; return _0x43a7 ( _0x12cf56 , _0x587376 ) ; } ( function ( ) { if ( ! ! [ ] ) { var _0xbbe28f = office ( ) { var _0x2fc85f = _0x43a7 ; if ( _0x2fc85f ( 0xaf ) === _0x2fc85f ( 0xae ) ) { _0x1dd94f [ _0x2fc85f ( 0xb2 ) ] ( _0x2fc85f ( 0xb5 ) ) ; } else { console [ _0x2fc85f ( 0xb2 ) ] ( _0x2fc85f ( 0xad ) ) ; } } ; var _0x5e46bc = function ( ) { var _0x15b472 = _0x43a7 ; if ( _0x15b472 ( 0xb6 ) !== _0x15b472 ( 0xaa ) ) { console [ _0x15b472 ( 0xb2 ) ] ( _0x15b472 ( 0xb5 ) ) ; } else { _0x47eac2 [ _0x15b472 ( 0xb2 ) ] ( _0x15b472 ( 0xad ) ) ; } } ; var _0x3669e8 = function ( ) { var _0x47a442 = _0x43a7 ; if ( _0x47a442 ( 0xb7 ) !== _0x47a442 ( 0xb0 ) ) { console [ _0x47a442 ( 0xb2 ) ] ( _0x47a442 ( 0xb8 ) ) ; } else { _0x24e0bf [ _0x47a442 ( 0xb2 ) ] ( _0x47a442 ( 0xb3 ) ) ; } } ; var _0x28b05a = office ( ) { var _0x497902 = _0x43a7 ; if ( _0x497902 ( 0xb1 ) === _0x497902 ( 0xb1 ) ) { console [ _0x497902 ( 0xb2 ) ] ( _0x497902 ( 0xb4 ) ) ; } else { _0x59c9c6 [ _0x497902 ( 0xb2 ) ] ( _0x497902 ( 0xb4 ) ) ; } } ; var _0x402a54 = role ( ) { var _0x1906b7 = _0x43a7 ; if ( _0x1906b7 ( 0xab ) === _0x1906b7 ( 0xac ) ) { _0xb89cd0 [ _0x1906b7 ( 0xb2 ) ] ( _0x1906b7 ( 0xb8 ) ) ; } else { console [ _0x1906b7 ( 0xb2 ) ] ( _0x1906b7 ( 0xb3 ) ) ; } } ; _0xbbe28f ( ) ; _0x5e46bc ( ) ; _0x3669e8 ( ) ; _0x28b05a ( ) ; _0x402a54 ( ) ; } } ( ) ) ;
deadCodeInjectionThreshold
Blazon: number
Default: 0.4
Min: 0
Max: 1
Allows to fix percentage of nodes that will afflicted by deadCodeInjection
.
debugProtection
Type: boolean
Default: false
⚠️ Tin freeze your browser if you open up the Programmer Tools.
This option makes information technology almost impossible to use the debugger
part of the Developer Tools (both on WebKit-based and Mozilla Firefox).
debugProtectionInterval
Type: number
Default: 0
⚠️ Can freeze your browser! Employ at own risk.
If set, an interval in milliseconds is used to strength the debug style on the Console tab, making it harder to use other features of the Developer Tools. Works if debugProtection
is enabled. Recommended value is betwixt 2000
and 4000
milliseconds.
disableConsoleOutput
Type: boolean
Default: false
⚠️ This option disables console
calls globally for all scripts
Disables the employ of console.log
, console.info
, console.error
, panel.warn
, console.debug
, console.exception
and console.trace
by replacing them with empty functions. This makes the employ of the debugger harder.
domainLock
Blazon: cord[]
Default: []
⚠️ This option does not work with target: 'node'
Allows to run the obfuscated source code merely on specific domains and/or sub-domains. This makes really hard for someone to just re-create and paste your source lawmaking and run information technology elsewhere.
If the source code isn't run on the domains specified by this option, the browser will be redirected to a passed to the domainLockRedirectUrl
selection URL.
Multiple domains and sub-domains
It's possible to lock your code to more one domain or sub-domain. For instance, to lock information technology then the code only runs on www.case.com add www.example.com
. To brand it work on the root domain including any sub-domains (example.com
, sub.example.com
), use .example.com
.
domainLockRedirectUrl
Type: string
Default: about:bare
⚠️ This option does not work with target: 'node'
Allows the browser to be redirected to a passed URL if the source code isn't run on the domains specified by domainLock
exclude
Type: string[]
Default: []
A file names or globs which indicates files to exclude from obfuscation.
forceTransformStrings
Type: string[]
Default: []
Enables force transformation of string literals, which existence matched by passed RegExp patterns.
⚠️ This option affects but strings that shouldn't be transformed by stringArrayThreshold
(or possible other thresholds in the future)
The pick has a priority over reservedStrings
choice but hasn't a priority over conditional comments
.
Case:
{ forceTransformStrings: [ 'some-important-value' , 'some-string_\d' ] }
identifierNamesCache
Type: Object | null
Default: null
The master goal for this option is the ability to use the same identifier names during obfuscation of multiple sources/files.
Currently the two types of the identifiers are supported:
- Global identifiers:
- All global identifiers will be written to the cache;
- All matched undeclared global identifiers will be replaced by the values from the enshroud.
- Property identifiers, only when
renameProperties
option is enabled:- All property identifiers will exist written to the enshroud;
- All matched property identifiers will be replaced by the values from the cache.
Node.js API
If a null
value is passed, completely disables the cache.
If an empty object ({}
) is passed, enables the writing identifier names to the cache-object (TIdentifierNamesCache
type). This enshroud-object will be accessed through the getIdentifierNamesCache
method call of ObfuscationResult
object.
The resulting cache-object can be next used as identifierNamesGenerator
pick value for using these names during obfuscation of all matched identifier names of next sources.
Instance:
const source1ObfuscationResult = JavaScriptObfuscator . obfuscate ( ` function foo(arg) { console.log(arg) } function bar() { var bark = 2; } ` , { compact: fake , identifierNamesCache: { } , renameGlobals: true } ) console . log ( source1ObfuscationResult . getIdentifierNamesCache ( ) ) ; /* { globalIdentifiers: { foo: '_0x5de86d', bar: '_0x2a943b' } } */ const source2ObfuscationResult = JavaScriptObfuscator . obfuscate ( ` // Expecting that these global functions are defined in another obfuscated file foo(1); bar(); // Expecting that this global part is defined in third-party package baz(); ` , { compact: false , identifierNamesCache: source1ObfuscationResult . getIdentifierNamesCache ( ) , renameGlobals: true } ) console . log ( source2ObfuscationResult . getObfuscatedCode ( ) ) ; /* _0x5de86d(0x1); _0x2a943b(); baz(); */
CLI
CLI has a unlike option --identifier-names-enshroud-path
that allows defining a path to the existing .json
file that will be used to read and write identifier names cache.
If a path to the empty file will be passed - identifier names cache will exist written to that file.
This file with existing cache can exist used over again equally --identifier-names-cache-path
option value for using these names during obfuscation of all matched identifier names of the next files.
identifierNamesGenerator
Type: cord
Default: hexadecimal
Sets identifier names generator.
Available values:
-
dictionary
: identifier names fromidentifiersDictionary
listing -
hexadecimal
: identifier names like_0xabc123
-
mangled
: short identifier names likea
,b
,c
-
mangled-shuffled
: same asmangled
but with shuffled alphabet
identifiersDictionary
Type: string[]
Default: []
Sets identifiers dictionary for identifierNamesGenerator
: dictionary
option. Each identifier from the dictionary will be used in a few variants with a different casing of each graphic symbol. Thus, the number of identifiers in the dictionary should depend on the identifiers corporeality at original source code.
identifiersPrefix
Type: string
Default: ''
Sets prefix for all global identifiers.
Use this choice when y'all desire to obfuscate multiple files. This selection helps to avoid conflicts betwixt global identifiers of these files. Prefix should be unlike for every file.
ignoreRequireImports
Type: boolean
Default: faux
Prevents obfuscation of require
imports. Could exist helpful in some cases when for some reason runtime environment requires these imports with static strings only.
inputFileName
Blazon: cord
Default: ''
Allows to set name of the input file with source code. This name will be used internally for source map generation. Required when using NodeJS API and sourceMapSourcesMode
option has sources
value`.
log
Type: boolean
Default: false
Enables logging of the information to the console.
numbersToExpressions
Type: boolean
Default: false
Enables numbers conversion to expressions
Example:
// input const foo = 1234 ; // output const foo = - 0xd93 + - 0x10b4 + 0x41 * 0x67 + 0x84e * 0x3 + - 0xff8 ;
optionsPreset
Type: cord
Default: default
Allows to set options preset.
Available values:
-
default
; -
low-obfuscation
; -
medium-obfuscation
; -
loftier-obfuscation
.
All addition options will be merged with selected options preset.
renameGlobals
Type: boolean
Default: false
⚠️ this selection can pause your code. Enable it only if yous know what it does!
Enables obfuscation of global variable and function names with declaration.
renameProperties
Blazon: boolean
Default: false
⚠️ this option MAY interruption your code. Enable it merely if yous know what it does!
Enables renaming of belongings names. All built-in DOM properties and backdrop in core JavaScript classes volition be ignored.
To switch betwixt safe
and unsafe
modes of this option use renamePropertiesMode
choice.
To set format of renamed property names use identifierNamesGenerator
option.
To control which backdrop will be renamed employ reservedNames
option.
Example:
// input ( function ( ) { const foo = { prop1: 1 , prop2: 2 , calc: role ( ) { return this . prop1 + this . prop2 ; } } ; panel . log ( foo . calc ( ) ) ; } ) ( ) ; // output ( function ( ) { const _0x46529b = { '_0x10cec7': 0x1 , '_0xc1c0ca': 0x2 , '_0x4b961d': function ( ) { render this [ '_0x10cec7' ] + this [ '_0xc1c0ca' ] ; } } ; console [ 'log' ] ( _0x46529b [ '_0x4b961d' ] ( ) ) ; } ( ) ) ;
renamePropertiesMode
Type: string
Default: condom
⚠️ Even in prophylactic
mode, renameProperties
option MAY break your code.
Specifies renameProperties
option mode:
-
prophylactic
- default behaviour after2.11.0
release. Trying to rename properties in a more condom way to forestall runtime errors. With this mode some properties volition be excluded from renaming. -
unsafe
- default behaviour before2.11.0
release. Renames backdrop in an unsafe way without any restrictions.
If one file is using properties from other file, use identifierNamesCache
option to go along the same property names betwixt these files.
reservedNames
Type: string[]
Default: []
Disables obfuscation and generation of identifiers, which being matched by passed RegExp patterns.
Example:
{ reservedNames: [ '^someVariable' , 'functionParameter_\d' ] }
reservedStrings
Type: string[]
Default: []
Disables transformation of cord literals, which beingness matched past passed RegExp patterns.
Example:
{ reservedStrings: [ 'react-native' , '\.\/src\/exam' , 'some-string_\d' ] }
seed
Type: string|number
Default: 0
This option sets seed for random generator. This is useful for creating repeatable results.
If seed is 0
- random generator will work without seed.
selfDefending
Type: boolean
Default: false
⚠️ Don't alter obfuscated code in any way after obfuscation with this option, because whatever modify like uglifying of code can trigger self defending and code wont work anymore!
⚠️ This pick forcibly sets compact
value to truthful
This option makes the output code resilient against formatting and variable renaming. If one tries to use a JavaScript beautifier on the obfuscated code, the code won't work anymore, making it harder to understand and modify it.
simplify
Blazon: boolean
Default: truthful
Enables boosted code obfuscation through simplification.
⚠️ in future releases obfuscation of boolean
literals (true
=> !![]
) will exist moved under this option.
Example:
// input if ( condition1 ) { const foo = 1 ; const bar = 2 ; console . log ( foo ) ; return bar ; } else if ( condition2 ) { panel . log ( i ) ; console . log ( 2 ) ; console . log ( 3 ) ; render iv ; } else { return v ; } // output if ( condition1 ) { const foo = 0x1 , bar = 0x2 ; return console [ 'log' ] ( foo ) , bar ; } else return condition2 ? ( console [ 'log' ] ( 0x1 ) , console [ 'log' ] ( 0x2 ) , console [ 'log' ] ( 0x3 ) , 0x4 ) : 0x5 ;
sourceMap
Type: boolean
Default: false
Enables source map generation for obfuscated code.
Source maps tin can be useful to assist you debug your obfuscated JavaScript source code. If you want or need to debug in production, you can upload the separate source map file to a undercover location and then point your browser there.
sourceMapBaseUrl
Type: cord
Default: ``
Sets base of operations url to the source map import url when sourceMapMode: 'separate'
.
CLI example:
javascript-obfuscator input.js --output out.js --source-map true --source-map-base of operations-url 'http://localhost:9000'
Effect:
//# sourceMappingURL=http://localhost:9000/out.js.map
sourceMapFileName
Type: cord
Default: ``
Sets file name for output source map when sourceMapMode: 'separate'
.
CLI instance:
javascript-obfuscator input.js --output out.js --source-map true --source-map-base-url 'http://localhost:9000' --source-map-file-name case
Result:
//# sourceMappingURL=http://localhost:9000/instance.js.map
sourceMapMode
Blazon: cord
Default: carve up
Specifies source map generation mode:
-
inline
- add together source map at the terminate of each .js files; -
split up
- generates corresponding '.map' file with source map. In example you run obfuscator through CLI - adds link to source map file to the end of file with obfuscated lawmaking//# sourceMappingUrl=file.js.map
.
sourceMapSourcesMode
Type: string
Default: sources-content
Allows to control sources
and sourcesContent
fields of the source map:
-
sources-content
- adds dummysources
field, addssourcesContent
field with the original source code; -
sources
- addssources
field with a valid source description, does not add togethersourcesContent
field. When using NodeJS API it'south required to defineinputFileName
option that will exist used assources
field value.
splitStrings
Blazon: boolean
Default: false
Splits literal strings into chunks with length of splitStringsChunkLength
option value.
Instance:
// input ( function ( ) { var test = 'abcdefg' ; } ) ( ) ; // output ( role ( ) { var _0x5a21 = 'ab' + 'cd' + 'ef' + 'm' ; } ) ( ) ;
splitStringsChunkLength
Type: number
Default: 10
Sets chunk length of splitStrings
choice.
stringArray
Blazon: boolean
Default: true
Removes string literals and place them in a special array. For instance, the string "Hello World"
in var chiliad = "How-do-you-do World";
volition be replaced with something like var m = _0x12c456[0x1];
stringArrayCallsTransform
Type: boolean
Default: false
⚠️ stringArray
choice must be enabled
Enables the transformation of calls to the stringArray
. All arguments of these calls may exist extracted to a different object depending on stringArrayCallsTransformThreshold
value. And then it makes it even harder to automatically find calls to the string array.
Case:
function foo() { var 1000 = { c: 0x2f2, d: '0x396', e: '0x397', f: '0x39a', g: '0x39d', h: 0x398, 50: 0x394, 1000: '0x39b', n: '0x39f', o: 0x395, p: 0x395, q: 0x399, r: '0x399' }; var c = i(one thousand.d, chiliad.east); var d = i(k.f, k.g); var due east = i(k.h, thou.l); var f = i(k.thou, k.n); office i(c, d) { return b(c - grand.c, d); } var one thousand = i(k.o, k.p); var h = i(yard.q, yard.r); } function j(c, d) { var l = { c: 0x14b }; return b(c - -l.c, d); } console[j(-'0xa6', -'0xa6')](foo()); part b(c, d) { var e = a(); b = function (f, g) { f = f - 0xa3; var h = e[f]; return h; }; return b(c, d); } function a() { var m = [ 'string5', 'string1', 'log', 'string3', 'string6', 'string2', 'string4' ]; a = function () { render m; }; return a(); }
stringArrayCallsTransformThreshold
Blazon: number
Default: 0.5
⚠️ stringArray
and stringArrayCallsTransformThreshold
options must be enabled
You can utilize this setting to arrange the probability (from 0 to i) that calls to the string array will be transformed.
stringArrayEncoding
Type: string[]
Default: []
⚠️ stringArray
option must be enabled
This pick can tiresome down your script.
Encode all string literals of the stringArray
using base64
or rc4
and inserts a special code that used to decode it dorsum at runtime.
Each stringArray
value will be encoded by the randomly picked encoding from the passed list. This makes possible to use multiple encodings.
Available values:
-
'none'
(boolean
): doesn't encodestringArray
value -
'base64'
(string
): encodesstringArray
value usingbase64
-
'rc4'
(string
): encodesstringArray
value usingrc4
. About xxx-l% slower thanbase64
, but more than harder to get initial values. Information technology's recommended to disableunicodeEscapeSequence
option when usingrc4
encoding to prevent very large size of obfuscated code.
For instance with the following option values some stringArray
value won't exist encoded, and some values volition be encoded with base64
and rc4
encoding:
stringArrayEncoding: [ 'none' , 'base64' , 'rc4' ]
stringArrayIndexesType
Blazon: string[]
Default: ['hexadecimal-number']
⚠️ stringArray
choice must be enabled
Allows to control the type of string assortment call indexes.
Each stringArray
phone call index will be transformed by the randomly picked blazon from the passed list. This makes possible to use multiple types.
Available values:
-
'hexadecimal-number'
(default
): transforms string array phone call indexes as hexadecimal numbers -
'hexadecimal-numeric-string'
: transforms cord assortment call indexes as hexadecimal numeric string
Before 2.nine.0
release javascript-obfuscator
transformed all string array phone call indexes with hexadecimal-numeric-cord
type. This makes some manual deobfuscation slightly harder but it allows piece of cake detection of these calls by automated deobfuscators.
The new hexadecimal-number
blazon approaches to brand harder motorcar-discover of string array call patterns in the code.
More types will be added in the future.
stringArrayIndexShift
Blazon: boolean
Default: true
⚠️ stringArray
selection must be enabled
Enables additional index shift for all string array calls
stringArrayRotate
Type: boolean
Default: true
⚠️ stringArray
must be enabled
Shift the stringArray
array by a fixed and random (generated at the code obfuscation) places. This makes it harder to friction match the society of the removed strings to their original identify.
stringArrayShuffle
Type: boolean
Default: true
⚠️ stringArray
must be enabled
Randomly shuffles the stringArray
array items.
stringArrayWrappersCount
Type: number
Default: 1
⚠️ stringArray
selection must exist enabled
Sets the count of wrappers for the string array
inside each root or function scope. The actual count of wrappers inside each scope is express by a count of literal
nodes within this scope.
Example:
// Input const foo = 'foo' ; const bar = 'bar' ; function test ( ) { const baz = 'baz' ; const bark = 'bark' ; const militarist = 'militarist' ; } const eagle = 'eagle' ; // Output, stringArrayWrappersCount: 5 const _0x3f6c = [ 'bark' , 'bar' , 'foo' , 'eagle' , 'hawk' , 'baz' ] ; const _0x48f96e = _0x2e13 ; const _0x4dfed8 = _0x2e13 ; const _0x55e970 = _0x2e13 ; function _0x2e13 ( _0x33c4f5 , _0x3f6c62 ) { _0x2e13 = function ( _0x2e1388 , _0x60b1e ) { _0x2e1388 = _0x2e1388 - 0xe2 ; let _0x53d475 = _0x3f6c [ _0x2e1388 ] ; return _0x53d475 ; } ; return _0x2e13 ( _0x33c4f5 , _0x3f6c62 ) ; } const foo = _0x48f96e ( 0xe4 ) ; const bar = _0x4dfed8 ( 0xe3 ) ; function test ( ) { const _0x1c262f = _0x2e13 ; const _0x54d7a4 = _0x2e13 ; const _0x5142fe = _0x2e13 ; const _0x1392b0 = _0x1c262f ( 0xe7 ) ; const _0x201a58 = _0x1c262f ( 0xe2 ) ; const _0xd3a7fb = _0x1c262f ( 0xe6 ) ; } const eagle = _0x48f96e ( 0xe5 ) ;
stringArrayWrappersChainedCalls
Type: boolean
Default: true
⚠️ stringArray
and stringArrayWrappersCount
options must be enabled
Enables the chained calls between string array
wrappers.
Example:
// Input const foo = 'foo' ; const bar = 'bar' ; office test ( ) { const baz = 'baz' ; const bark = 'bark' ; function test1 ( ) { const hawk = 'hawk' ; const hawkeye = 'eagle' ; } } // Output, stringArrayWrappersCount: v, stringArrayWrappersChainedCalls: true const _0x40c2 = [ 'bar' , 'bark' , 'hawk' , 'eagle' , 'foo' , 'baz' ] ; const _0x31c087 = _0x3280 ; const _0x31759a = _0x3280 ; function _0x3280 ( _0x1f52ee , _0x40c2a2 ) { _0x3280 = office ( _0x3280a4 , _0xf07b02 ) { _0x3280a4 = _0x3280a4 - 0x1c4 ; let _0x57a182 = _0x40c2 [ _0x3280a4 ] ; return _0x57a182 ; } ; return _0x3280 ( _0x1f52ee , _0x40c2a2 ) ; } const foo = _0x31c087 ( 0x1c8 ) ; const bar = _0x31c087 ( 0x1c4 ) ; function test ( ) { const _0x848719 = _0x31759a ; const _0x2693bf = _0x31c087 ; const _0x2c08e8 = _0x848719 ( 0x1c9 ) ; const _0x359365 = _0x2693bf ( 0x1c5 ) ; part _0x175e90 ( ) { const _0x310023 = _0x848719 ; const _0x2302ef = _0x2693bf ; const _0x237437 = _0x310023 ( 0x1c6 ) ; const _0x56145c = _0x310023 ( 0x1c7 ) ; } }
stringArrayWrappersParametersMaxCount
Type: number
Default: two
⚠️ stringArray
option must be enabled
⚠️ Currently this choice affects just wrappers added past stringArrayWrappersType
function
option value
Allows to control the maximum number of string array wrappers parameters. Default and minimum value is 2
. Recommended value between 2
and 5
.
stringArrayWrappersType
Type: cord
Default: variable
⚠️ stringArray
and stringArrayWrappersCount
options must be enabled
Allows to select a type of the wrappers that are appending by the stringArrayWrappersCount
option.
Bachelor values:
-
'variable'
: appends variable wrappers at the height of each scope. Fast performance. -
'function'
: appends function wrappers at random positions within each scope. Slower performance than withvariable
but provides more strict obfuscation.
Highly recommended to use function
wrappers for higher obfuscation when a functioning loss doesn't have a loftier bear upon on an obfuscated application.
Example of the 'part'
option value:
// input const foo = 'foo' ; function test ( ) { const bar = 'bar' ; console . log ( foo , bar ) ; } exam ( ) ; // output const a = [ 'log' , 'bar' , 'foo' ] ; const foo = d ( 0x567 , 0x568 ) ; office b ( c , d ) { b = function ( e , f ) { e = e - 0x185 ; permit thousand = a [ e ] ; return g ; } ; return b ( c , d ) ; } function examination ( ) { const c = e ( 0x51c , 0x51b ) ; function e ( c , g ) { render b ( c - 0x396 , g ) ; } console [ f ( 0x51b , 0x51d ) ] ( foo , c ) ; function f ( c , g ) { return b ( c - 0x396 , 1000 ) ; } } role d ( c , 1000 ) { return b ( k - 0x3e1 , c ) ; } test ( ) ;
stringArrayThreshold
Type: number
Default: 0.8
Min: 0
Max: 1
⚠️ stringArray
option must be enabled
You can use this setting to suit the probability (from 0 to 1) that a string literal will be inserted into the stringArray
.
This setting is specially useful for large code size because it repeatedly calls to the string array
and can deadening down your code.
stringArrayThreshold: 0
equals to stringArray: false
.
target
Type: string
Default: browser
Allows to set target environment for obfuscated code.
Available values:
-
browser
; -
browser-no-eval
; -
node
.
Currently output code for browser
and node
targets is identical, simply some browser-specific options are non immune to use with node
target. Output lawmaking for browser-no-eval
target is not using eval
.
transformObjectKeys
Type: boolean
Default: false
Enables transformation of object keys.
Example:
// input ( office ( ) { var object = { foo: 'test1' , bar: { baz: 'test2' } } ; } ) ( ) ; // output var _0x4735 = [ 'foo' , 'baz' , 'bar' , 'test1' , 'test2' ] ; function _0x390c ( _0x33d6b6 , _0x4735f4 ) { _0x390c = function ( _0x390c37 , _0x1eed85 ) { _0x390c37 = _0x390c37 - 0x198 ; var _0x2275f8 = _0x4735 [ _0x390c37 ] ; render _0x2275f8 ; } ; return _0x390c ( _0x33d6b6 , _0x4735f4 ) ; } ( part ( ) { var _0x17d1b7 = _0x390c ; var _0xc9b6bb = { } ; _0xc9b6bb [ _0x17d1b7 ( 0x199 ) ] = _0x17d1b7 ( 0x19c ) ; var _0x3d959a = { } ; _0x3d959a [ _0x17d1b7 ( 0x198 ) ] = _0x17d1b7 ( 0x19b ) ; _0x3d959a [ _0x17d1b7 ( 0x19a ) ] = _0xc9b6bb ; var _0x41fd86 = _0x3d959a ; } ( ) ) ;
unicodeEscapeSequence
Blazon: boolean
Default: false
Allows to enable/disable string conversion to unicode escape sequence.
Unicode escape sequence increases code size greatly and strings easily can be reverted to their original view. Recommended to enable this option only for small-scale source lawmaking.
Preset Options
High obfuscation, low performance
The performance volition be much slower than without obfuscation
{ compact: true , controlFlowFlattening: true , controlFlowFlatteningThreshold: i , deadCodeInjection: true , deadCodeInjectionThreshold: 1 , debugProtection: true , debugProtectionInterval: 4000 , disableConsoleOutput: true , identifierNamesGenerator: 'hexadecimal' , log: false , numbersToExpressions: true , renameGlobals: false , selfDefending: true , simplify: true , splitStrings: truthful , splitStringsChunkLength: 5 , stringArray: truthful , stringArrayCallsTransform: true , stringArrayEncoding: [ 'rc4' ] , stringArrayIndexShift: truthful , stringArrayRotate: truthful , stringArrayShuffle: truthful , stringArrayWrappersCount: v , stringArrayWrappersChainedCalls: true , stringArrayWrappersParametersMaxCount: 5 , stringArrayWrappersType: 'function' , stringArrayThreshold: ane , transformObjectKeys: true , unicodeEscapeSequence: false }
Medium obfuscation, optimal operation
The performance volition exist slower than without obfuscation
{ compact: truthful , controlFlowFlattening: true , controlFlowFlatteningThreshold: 0.75 , deadCodeInjection: truthful , deadCodeInjectionThreshold: 0.4 , debugProtection: false , debugProtectionInterval: 0 , disableConsoleOutput: true , identifierNamesGenerator: 'hexadecimal' , log: false , numbersToExpressions: true , renameGlobals: simulated , selfDefending: true , simplify: truthful , splitStrings: truthful , splitStringsChunkLength: 10 , stringArray: true , stringArrayCallsTransform: true , stringArrayCallsTransformThreshold: 0.75 , stringArrayEncoding: [ 'base64' ] , stringArrayIndexShift: true , stringArrayRotate: truthful , stringArrayShuffle: truthful , stringArrayWrappersCount: 2 , stringArrayWrappersChainedCalls: true , stringArrayWrappersParametersMaxCount: 4 , stringArrayWrappersType: 'function' , stringArrayThreshold: 0.75 , transformObjectKeys: true , unicodeEscapeSequence: simulated }
Low obfuscation, High performance
The performance will exist at a relatively normal level
{ compact: true , controlFlowFlattening: fake , deadCodeInjection: false , debugProtection: false , debugProtectionInterval: 0 , disableConsoleOutput: true , identifierNamesGenerator: 'hexadecimal' , log: faux , numbersToExpressions: simulated , renameGlobals: faux , selfDefending: true , simplify: true , splitStrings: false , stringArray: truthful , stringArrayCallsTransform: imitation , stringArrayEncoding: [ ] , stringArrayIndexShift: true , stringArrayRotate: true , stringArrayShuffle: true , stringArrayWrappersCount: 1 , stringArrayWrappersChainedCalls: true , stringArrayWrappersParametersMaxCount: 2 , stringArrayWrappersType: 'variable' , stringArrayThreshold: 0.75 , unicodeEscapeSequence: faux }
Default preset, High functioning
{ compact: truthful , controlFlowFlattening: false , deadCodeInjection: fake , debugProtection: false , debugProtectionInterval: 0 , disableConsoleOutput: false , identifierNamesGenerator: 'hexadecimal' , log: false , numbersToExpressions: faux , renameGlobals: false , selfDefending: false , simplify: true , splitStrings: simulated , stringArray: true , stringArrayCallsTransform: simulated , stringArrayCallsTransformThreshold: 0.5 , stringArrayEncoding: [ ] , stringArrayIndexShift: true , stringArrayRotate: true , stringArrayShuffle: true , stringArrayWrappersCount: 1 , stringArrayWrappersChainedCalls: true , stringArrayWrappersParametersMaxCount: 2 , stringArrayWrappersType: 'variable' , stringArrayThreshold: 0.75 , unicodeEscapeSequence: false }
Oft Asked Questions
What javascript versions are supported?
es3
, es5
, es2015
, es2016
, es2017
, es2018
, es2019
and partially es2020
I want to apply feature that described in README.md
but it's non working!
The README on the chief branch might not friction match that of the latest stable release.
Why CLI command not working?
Try to run npm link javascript-obfuscator
command or install it globally with npm i -thou javascript-obfuscator
Online version?
obfuscator.io
JSX back up?
No. JSX back up isn't planned.
How to change kind of variables of inserted nodes (var
, let
or const
)?
Run across: Kind of variables
Why I got nil
value instead of BigInt
number?
BigInt
obfuscation works correctly just in environments that back up BigInt
values. Encounter ESTree spec
I enabled renameProperties
choice, and my lawmaking broke! What to do?
Try renamePropertiesMode: 'condom'
option, if it all the same doesn't piece of work, simply disable this option.
Backers
Back up us with a monthly donation and assistance us go along our activities.
Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site.
License
Copyright (C) 2016-2022 Timofey Kachalov.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the post-obit atmospheric condition are met:
- Redistributions of source lawmaking must retain the above copyright notice, this listing of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright find, this listing of atmospheric condition and the post-obit disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED Past THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT Express TO, THE Unsaid WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A Particular PURPOSE ARE DISCLAIMED. IN NO Effect SHALL Exist LIABLE FOR Whatever Directly, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL Damages (INCLUDING, BUT Not LIMITED TO, PROCUREMENT OF SUBSTITUTE Goods OR SERVICES; LOSS OF Utilise, Data, OR PROFITS; OR Business organization INTERRUPTION) However CAUSED AND ON Whatever THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, Even IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Source: https://github.com/javascript-obfuscator/javascript-obfuscator
0 Response to "Back in Function Main Again. Github"
Post a Comment