选项参考
目录
compact
Type: boolean Default: true
将输出代码压缩到一行。
config
Type: string Default: ``
包含混淆器选项的 JS/JSON 配置文件名。这些选项会被直接传给 CLI 的选项覆盖。
controlFlowFlattening
Type: boolean Default: false
⚠️ 该选项会显著影响性能,运行时速度最多可下降 1.5 倍。使用 controlFlowFlatteningThreshold 设置受控制流平坦化影响的节点百分比。
启用代码控制流平坦化。控制流平坦化是对源代码结构的一种转换,会妨碍对程序的理解。
示例:
// input
(function(){
function foo () {
return function () {
var sum = 1 + 2;
console.log(1);
console.log(2);
console.log(3);
console.log(4);
console.log(5);
console.log(6);
}
}
foo()();
})();
// output
(function () {
function _0x3bfc5c() {
return function () {
var _0x3260a5 = {
'WtABe': '4|0|6|5|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);
continue;
case '2':
console['log'](0x5);
continue;
case '3':
console['log'](0x4);
continue;
case '4':
var _0x1f2f2f = _0x3260a5['GokKo'](0x1, 0x2);
continue;
case '5':
console['log'](0x3);
continue;
case '6':
console['log'](0x2);
continue;
}
break;
}
};
}
_0x3bfc5c()();
}());
controlFlowFlatteningThreshold
Type: number Default: 0.75 Min: 0 Max: 1
controlFlowFlattening 转换应用到任一给定节点的概率。
对于代码体量较大的情况,此设置尤其有用,因为大量控制流转换会拖慢代码运行速度并增大代码体积。
controlFlowFlatteningThreshold: 0 等价于 controlFlowFlattening: false。
deadCodeInjection
Type: boolean Default: false
⚠️ 会大幅增加混淆后代码的体积(最多可达 200%),仅在不在意混淆后代码体积时使用。使用 deadCodeInjectionThreshold 设置受无用代码注入影响的节点百分比。
⚠️ 该选项会强制启用 stringArray 选项。
⚠️ 启用 vmObfuscation 时,该选项会被静默禁用。
启用该选项后,随机的无用代码块会被添加到混淆后的代码中。
示例:
// input
(function(){
if (true) {
var foo = function () {
console.log('abc');
};
var bar = function () {
console.log('def');
};
var baz = function () {
console.log('ghi');
};
var bark = function () {
console.log('jkl');
};
var hawk = function () {
console.log('mno');
};
foo();
bar();
baz();
bark();
hawk();
}
})();
// output
var _0x37b8 = [
'YBCtz',
'GlrkA',
'urPbb',
'abc',
'NMIhC',
'yZgAj',
'zrAId',
'EtyJA',
'log',
'mno',
'jkl',
'def',
'Quzya',
'IWbBa',
'ghi'
];
function _0x43a7(_0x12cf56, _0x587376) {
_0x43a7 = function (_0x2f87a8, _0x47eac2) {
_0x2f87a8 = _0x2f87a8 - (0x16a7 * 0x1 + 0x5 * 0x151 + -0x1c92);
var _0x341e03 = _0x37b8[_0x2f87a8];
return _0x341e03;
};
return _0x43a7(_0x12cf56, _0x587376);
}
(function () {
if (!![]) {
var _0xbbe28f = function () {
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 = function () {
var _0x497902 = _0x43a7;
if (_0x497902(0xb1) === _0x497902(0xb1)) {
console[_0x497902(0xb2)](_0x497902(0xb4));
} else {
_0x59c9c6[_0x497902(0xb2)](_0x497902(0xb4));
}
};
var _0x402a54 = function () {
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
Type: number Default: 0.4 Min: 0 Max: 1
允许设置受 deadCodeInjection 影响的节点百分比。
debugProtection
Type: boolean Default: false
⚠️ 如果您打开开发者工具,可能会导致浏览器卡死。
⚠️ 启用 vmObfuscation 时,该选项会被静默禁用。请改用 vmDebugProtection。
该选项几乎让开发者工具的 debugger 功能无法使用(在基于 WebKit 的浏览器和 Mozilla Firefox 上均是如此)。
debugProtectionInterval
Type: number Default: 0
⚠️ 可能会导致浏览器卡死!使用需自担风险。
⚠️ 启用 vmObfuscation 时,该选项会被静默禁用。请改用 vmDebugProtection。
如果设置了该值,将以毫秒为单位使用一个间隔,在 Console 标签页上强制进入调试模式,从而增加使用开发者工具其他功能的难度。仅在启用 debugProtection 时生效。推荐取值在 2000 到 4000 毫秒之间。
disableConsoleOutput
Type: boolean Default: false
⚠️ 该选项会在全局范围内为所有脚本禁用 console 调用
通过将 console.log、console.info、console.error、console.warn、console.debug、console.exception 和 console.trace 替换为空函数来禁用它们的使用。这会增加使用调试器的难度。
domainLock
Type: string[] Default: []
⚠️ 该选项不适用于 target: 'node'、target: 'service-worker' 或 target: 'bytenode'
允许混淆后的源代码只在特定的域名和/或子域名上运行。这会让别人很难直接复制粘贴您的源代码然后在别处运行。
如果源代码没有在该选项指定的域名上运行,浏览器将被重定向到传给 domainLockRedirectUrl 选项的 URL。
多个域名和子域名
您可以将代码锁定到多个域名或子域名。例如,要将代码锁定为只在 www.example.com 上运行,添加 www.example.com。要让它在根域名及其任意子域名(example.com、sub.example.com)上生效,使用 .example.com。
domainLockRedirectUrl
Type: string Default: about:blank
⚠️ 该选项不适用于 target: 'node'、target: 'service-worker' 或 target: 'bytenode'
如果源代码没有在 domainLock 指定的域名上运行,允许将浏览器重定向到所传入的 URL。
exclude
Type: string[] Default: []
用于指定要排除在混淆之外的文件的文件名或 glob 模式。
forceTransformStrings
Type: string[] Default: []
对与所传入正则表达式模式匹配的字符串字面量启用强制转换。
⚠️ 该选项只影响那些本不应被 stringArrayThreshold(或未来可能出现的其他阈值)转换的字符串
该选项的优先级高于 reservedStrings 选项,但不高于 conditional comments。
示例:
{
forceTransformStrings: [
'some-important-value',
'some-string_\d'
]
}
identifierNamesCache
Type: Object | null Default: null
该选项的主要目的是让您在混淆多个源码/文件时能够使用相同的标识符名称。
目前支持两类标识符:
- 全局标识符:
- 所有全局标识符都会被写入缓存;
- 所有匹配到的未声明全局标识符都会被替换为缓存中的值。
- 属性标识符,仅在启用
renameProperties选项时:- 所有属性标识符都会被写入缓存;
- 所有匹配到的属性标识符都会被替换为缓存中的值。
Node.js API
如果传入 null 值,则完全禁用缓存。
如果传入空对象({}),则启用将标识符名称写入缓存对象(TIdentifierNamesCache 类型)。该缓存对象可通过 ObfuscationResult 对象的 getIdentifierNamesCache 方法调用来访问。
得到的缓存对象随后可作为 identifierNamesGenerator 选项的值,用于在混淆后续源码时对所有匹配的标识符名称复用这些名称。
示例:
const source1ObfuscationResult = JavaScriptObfuscator.obfuscate(
`
function foo(arg) {
console.log(arg)
}
function bar() {
var bark = 2;
}
`,
{
compact: false,
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 function is defined in third-party package
baz();
`,
{
compact: false,
identifierNamesCache: source1ObfuscationResult.getIdentifierNamesCache(),
renameGlobals: true
}
)
console.log(source2ObfuscationResult.getObfuscatedCode());
/*
_0x5de86d(0x1);
_0x2a943b();
baz();
*/
CLI
CLI 提供了另一个选项 --identifier-names-cache-path,允许指定一个现有 .json 文件的路径,用于读写标识符名称缓存。
如果传入的是一个空文件的路径,标识符名称缓存会被写入该文件。
这个包含已有缓存的文件可以再次作为 --identifier-names-cache-path 选项的值,用于在混淆后续文件时对所有匹配的标识符名称复用这些名称。
identifierNamesGenerator
Type: string Default: hexadecimal
设置标识符名称生成器。
可用值:
dictionary:使用identifiersDictionary列表中的标识符名称hexadecimal:形如_0xabc123的标识符名称mangled:形如a、b、c的短标识符名称mangled-shuffled:与mangled相同,但打乱了字母表顺序
identifiersDictionary
Type: string[] Default: []
为 identifierNamesGenerator:dictionary 选项设置标识符字典。字典中的每个标识符都会以若干变体形式使用,每个字符采用不同的大小写。因此,字典中标识符的数量应根据原始源代码中标识符的数量来确定。
identifiersPrefix
Type: string Default: ''
为所有全局标识符设置前缀。
当您需要混淆多个文件时使用此选项。它有助于避免这些文件的全局标识符之间发生冲突。每个文件的前缀应各不相同。
randomIdentifiersPrefix
Type: boolean Default: false
为所有全局标识符追加一个基于种子的随机前缀(6 个字母数字字符)。当多个分别混淆的包被加载到同一个全局作用域中时,使用此选项可避免它们之间发生冲突 - 它免去了您手动为每个包挑选唯一 identifiersPrefix 的麻烦。
- 该随机值由
seed选项和源代码哈希派生而来,因此使用相同种子的可重现构建会生成相同的前缀。 - 当与
identifiersPrefix结合使用时,随机字符会追加在用户提供的前缀之后(例如myApp+ 随机aBc123→myAppaBc123)。 - 当与
vmObfuscation结合使用时,随机值会替换默认的vm前缀 - 随机性本身已能保证唯一性。
ignoreImports
Type: boolean Default: false
防止对 require 导入进行混淆。在某些情况下,当运行时环境出于某种原因要求这些导入必须使用静态字符串时,这会很有帮助。
inputFileName
Type: string Default: ''
允许设置包含源代码的输入文件名。该名称会在内部用于生成 source map。
当使用 NodeJS API 且 sourceMapSourcesMode 选项取值为 sources 时,此项为必填。
log
Type: boolean Default: false
启用将信息记录到控制台。
numbersToExpressions
Type: boolean Default: false
启用将数字转换为表达式。
示例:
// input
const foo = 1234;
// output
const foo=-0xd93+-0x10b4+0x41*0x67+0x84e*0x3+-0xff8;
optionsPreset
Type: string Default: default
允许设置选项预设。
可用值:
vm-default;vm-low-obfuscation;vm-medium-obfuscation;vm-high-obfuscation;vm-ultra-high-obfuscation;default;low-obfuscation;medium-obfuscation;high-obfuscation。
所有额外选项都会与所选的选项预设合并。
parseHtml
Type: boolean Default: false
启用对 HTML <script> 标签内 JavaScript 的混淆。
启用后,混淆器将会:
- 自动检测输入是否为 HTML(通过检查是否存在
<!DOCTYPE、<html>、<head>、<body>或<script>标签) - 从带有
data-javascript-obfuscator属性的<script>标签中提取 JavaScript - 在保留 HTML 结构的同时,单独混淆每个被标记的脚本
- 将混淆后的代码注入回原来的位置
重要: 只有带 data-javascript-obfuscator 属性的脚本才会被混淆。每个被标记的脚本都是单独且相互独立地混淆的。这意味着:
- 被标记脚本标签内的代码必须相互隔离 - 它不得引用其他被标记脚本标签中定义的变量、函数或类
- 未被标记的脚本仍可访问被标记脚本定义的全局变量(通过
var声明或显式的globalThis赋值) - 这让您可以明确控制要保护哪些脚本
会被混淆(必须带 data-javascript-obfuscator 属性):
<script data-javascript-obfuscator>- 普通脚本<script type="text/javascript" data-javascript-obfuscator>- 显式标注类型的脚本- 带有任意额外属性的脚本(
id、class、其他data-*等)
会被跳过(保持不变):
- 没有
data-javascript-obfuscator属性的脚本 <script type="module">- ES 模块(即便带有该属性)<script src="...">- 外部脚本(即便带有该属性)- 空的 script 标签
注意: 启用 parseHtml 时不会生成 source map,因为它们无法正确映射到 HTML 输出。
示例:
// input
const html = `<!DOCTYPE html>
<html>
<body>
<!-- This script will NOT be obfuscated -->
<script>
var helper = 'utility';
</script>
<!-- This script WILL be obfuscated -->
<script data-javascript-obfuscator>
var greeting = 'Hello World';
console.log(greeting);
</script>
</body>
</html>`;
JavaScriptObfuscator.obfuscate(html, {
parseHtml: true,
stringArray: true
});
// output: HTML with only the marked script obfuscated
renameGlobals
Type: boolean Default: false
⚠️ 该选项可能破坏您的代码。只有在您了解其作用时才启用它!
启用对全局变量名和函数名的混淆,连同其声明一起。
当该选项被禁用,且输入代码在全局作用域中声明了函数或类(即代码未被包裹在 IIFE 中)时,它们的名称会在混淆后的输出中原样保留 - 其他脚本可以通过名称引用它们。在 vmObfuscation 下,会报告一条列出这些名称的 VMGlobalFunctionNamesNotRenamed 警告,因为虽然函数体被隐藏为字节码,但可读的顶层名称仍会暴露代码的用途(例如暴露给 LLM)。要避免这种暴露,请将代码包裹在 IIFE 中,或启用此选项。
renameProperties
Type: boolean Default: false
⚠️ 该选项可能破坏您的代码。只有在您了解其作用时才启用它!
启用对属性名的重命名。所有内置 DOM 属性以及 JavaScript 核心类中的属性都会被忽略。
要在该选项的 safe 和 unsafe 模式之间切换,请使用 renamePropertiesMode 选项。
要设置重命名后属性名的格式,请使用 identifierNamesGenerator 选项。
要控制哪些属性会被重命名,请使用 reservedNames 选项。
示例:
// input
(function () {
const foo = {
prop1: 1,
prop2: 2,
calc: function () {
return this.prop1 + this.prop2;
}
};
console.log(foo.calc());
})();
// output
(function () {
const _0x46529b = {
'_0x10cec7': 0x1,
'_0xc1c0ca': 0x2,
'_0x4b961d': function () {
return this['_0x10cec7'] + this['_0xc1c0ca'];
}
};
console['log'](_0x46529b['_0x4b961d']());
}());
renamePropertiesMode
Type: string Default: safe
⚠️ 即便处于 safe 模式,renameProperties 选项也可能破坏您的代码。
指定 renameProperties 选项的模式:
safe-2.11.0版本之后的默认行为。尝试以更安全的方式重命名属性以避免运行时错误。在此模式下,部分属性会被排除在重命名之外。unsafe-2.11.0版本之前的默认行为。以不安全的方式重命名属性,不加任何限制。
如果一个文件使用了另一个文件中的属性,请使用 identifierNamesCache 选项,让这些文件之间保持相同的属性名。
reservedNames
Type: string[] Default: []
对与所传入正则表达式模式匹配的标识符,禁用混淆和生成。
示例:
{
reservedNames: [
'^someVariable',
'functionParameter_\d'
]
}
reservedStrings
Type: string[] Default: []
对与所传入正则表达式模式匹配的字符串字面量,禁用其转换。匹配到的字符串会在混淆后的输出中保持可见。
在使用 VM 混淆时,保留字符串会存放在一个单独的未加密数组中以保持可见。这对于必须保持可读的字符串很有用,例如用于监控的 API 端点或库标识符。
示例:
{
reservedStrings: [
'react-native',
'\.\/src\/test',
'some-string_\d'
]
}
seed
Type: string|number Default: 0
该选项为随机数生成器设置种子。这对于生成可重复的结果很有用。
如果 seed 为 0,随机数生成器将不使用种子运行。
selfDefending
Type: boolean Default: false
⚠️ 使用该选项混淆后,请勿以任何方式改动混淆后的代码,因为任何改动(如对代码进行 uglify 压缩)都可能触发自我防护,导致代码无法再运行!
⚠️ 该选项会强制将 compact 的值设为 true
⚠️ 启用 vmObfuscation 时,该选项会被静默禁用。请改用 vmSelfDefending。
该选项让输出代码能够抵御格式化和变量重命名。如果有人试图对混淆后的代码使用 JavaScript 美化工具,代码将无法再运行,从而增加理解和修改它的难度。
simplify
Type: boolean Default: true
通过简化启用额外的代码混淆。
⚠️ 在未来的版本中,对 boolean 字面量的混淆(true => !![])将被移到此选项之下。
示例:
// input
if (condition1) {
const foo = 1;
const bar = 2;
console.log(foo);
return bar;
} else if (condition2) {
console.log(1);
console.log(2);
console.log(3);
return 4;
} else {
return 5;
}
// 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
为混淆后的代码启用 source map 生成。
source map 有助于您调试混淆后的 JavaScript 源代码。如果您想要或需要在生产环境中调试,可以将单独的 source map 文件上传到一个隐秘位置,然后让浏览器指向那里。
sourceMapBaseUrl
Type: string Default: ``
当 sourceMapMode: 'separate' 时,为 source map 的引入 url 设置基础 url。
CLI 示例:
javascript-obfuscator input.js --output out.js --source-map true --source-map-base-url 'http://localhost:9000'
结果:
//# sourceMappingURL=http://localhost:9000/out.js.map
sourceMapFileName
Type: string Default: ``
当 sourceMapMode: 'separate' 时,为输出的 source map 设置文件名。
CLI 示例:
javascript-obfuscator input.js --output out.js --source-map true --source-map-base-url 'http://localhost:9000' --source-map-file-name example
结果:
//# sourceMappingURL=http://localhost:9000/example.js.map
sourceMapMode
Type: string Default: separate
指定 source map 的生成模式:
inline- 将 source map 追加到每个 .js 文件的末尾;separate- 生成对应的 '.map' 文件来存放 source map。如果您通过 CLI 运行混淆器,还会在混淆后代码文件的末尾添加指向 source map 文件的链接//# sourceMappingUrl=file.js.map。
sourceMapSourcesMode
Type: string Default: sources-content
允许控制 source map 的 sources 和 sourcesContent 字段:
sources-content- 添加一个占位的sources字段,并添加包含原始源代码的sourcesContent字段;sources- 添加带有有效源描述的sources字段,不添加sourcesContent字段。使用 NodeJS API 时,必须定义inputFileName选项,其值将用作sources字段的值。
splitStrings
Type: boolean Default: false
将字面量字符串拆分成长度为 splitStringsChunkLength 选项值的若干块。
示例:
// input
(function(){
var test = 'abcdefg';
})();
// output
(function(){
var _0x5a21 = 'ab' + 'cd' + 'ef' + 'g';
})();
splitStringsChunkLength
Type: number Default: 10
设置 splitStrings 选项的分块长度。
stringArray
Type: boolean Default: true
移除字符串字面量并将它们放入一个特殊数组中。例如,var m = "Hello World"; 中的字符串 "Hello World" 会被替换为类似 var m = _0x12c456[0x1]; 的形式。
stringArrayCallsTransform
Type: boolean Default: false
⚠️ 必须启用 stringArray 选项
启用对 stringArray 调用的转换。根据 stringArrayCallsTransformThreshold 的值,这些调用的所有参数可能会被提取到另一个对象中。
这样会进一步增加自动查找字符串数组调用的难度。
示例:
function foo() {
var k = {
c: 0x2f2,
d: '0x396',
e: '0x397',
f: '0x39a',
g: '0x39d',
h: 0x398,
l: 0x394,
m: '0x39b',
n: '0x39f',
o: 0x395,
p: 0x395,
q: 0x399,
r: '0x399'
};
var c = i(k.d, k.e);
var d = i(k.f, k.g);
var e = i(k.h, k.l);
var f = i(k.m, k.n);
function i(c, d) {
return b(c - k.c, d);
}
var g = i(k.o, k.p);
var h = i(k.q, k.r);
}
function j(c, d) {
var l = { c: 0x14b };
return b(c - -l.c, d);
}
console[j(-'0xa6', -'0xa6')](foo());
function 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 () {
return m;
};
return a();
}
stringArrayCallsTransformThreshold
Type: number Default: 0.5
⚠️ 必须启用 stringArray 和 stringArrayCallsTransformThreshold 选项
您可以用此设置来调整字符串数组调用被转换的概率(从 0 到 1)。
stringArrayEncoding
Type: string[] Default: []
⚠️ 必须启用 stringArray 选项
该选项可能会拖慢您的脚本。
使用 base64 或 rc4 对 stringArray 的所有字符串字面量进行编码,并插入一段用于在运行时将其解码还原的特殊代码。
每个 stringArray 值都会被从所传列表中随机选取的编码方式进行编码。这样就可以使用多种编码。
可用值:
'none'(boolean):不对stringArray值进行编码'base64'(string):使用base64对stringArray值进行编码'rc4'(string):使用rc4对stringArray值进行编码。比base64慢约 30-50%,但更难获取初始值。
例如,使用以下选项值时,部分 stringArray 值不会被编码,另一些值则会使用 base64 和 rc4 编码:
stringArrayEncoding: [
'none',
'base64',
'rc4'
]
stringArrayIndexesType
Type: string[] Default: ['hexadecimal-number']
⚠️ 必须启用 stringArray 选项
允许控制字符串数组调用索引的类型。
每个 stringArray 调用索引都会被从所传列表中随机选取的类型进行转换。这样就可以使用多种类型。
可用值:
'hexadecimal-number'(default):将字符串数组调用索引转换为十六进制数字'hexadecimal-numeric-string':将字符串数组调用索引转换为十六进制数字字符串
在 2.9.0 版本之前,javascript-obfuscator 会用 hexadecimal-numeric-string 类型转换所有字符串数组调用索引。这会让一些手动反混淆稍微困难一些,但也让自动反混淆器很容易检测到这些调用。
新的 hexadecimal-number 类型旨在让代码中字符串数组调用模式更难被自动检测。
未来会加入更多类型。
stringArrayIndexShift
Type: boolean Default: true
⚠️ 必须启用 stringArray 选项
为所有字符串数组调用启用额外的索引偏移。
stringArrayRotate
Type: boolean Default: true
⚠️ 必须启用 stringArray
将 stringArray 数组按一个固定且随机(在代码混淆时生成)的位数进行移位。这会增加将被移除字符串的顺序与其原始位置对应起来的难度。
stringArrayShuffle
Type: boolean Default: true
⚠️ 必须启用 stringArray
随机打乱 stringArray 数组中各项的顺序。
stringArrayWrappersCount
Type: number Default: 1
⚠️ 必须启用 stringArray 选项
设置每个根作用域或函数作用域内 string array 的包装器数量。
每个作用域内实际的包装器数量受该作用域内 literal 节点数量的限制。
示例:
// Input
const foo = 'foo';
const bar = 'bar';
function test () {
const baz = 'baz';
const bark = 'bark';
const hawk = 'hawk';
}
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 和 stringArrayWrappersCount 选项
启用 string array 包装器之间的链式调用。
示例:
// Input
const foo = 'foo';
const bar = 'bar';
function test () {
const baz = 'baz';
const bark = 'bark';
function test1() {
const hawk = 'hawk';
const eagle = 'eagle';
}
}
// Output, stringArrayWrappersCount: 5, stringArrayWrappersChainedCalls: true
const _0x40c2 = [
'bar',
'bark',
'hawk',
'eagle',
'foo',
'baz'
];
const _0x31c087 = _0x3280;
const _0x31759a = _0x3280;
function _0x3280(_0x1f52ee, _0x40c2a2) {
_0x3280 = function (_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);
function _0x175e90() {
const _0x310023 = _0x848719;
const _0x2302ef = _0x2693bf;
const _0x237437 = _0x310023(0x1c6);
const _0x56145c = _0x310023(0x1c7);
}
}
stringArrayWrappersParametersMaxCount
Type: number Default: 2
⚠️ 必须启用 stringArray 选项
⚠️ 目前该选项仅影响由 stringArrayWrappersType function 选项值添加的包装器
允许控制字符串数组包装器参数的最大数量。
默认值和最小值为 2。推荐取值在 2 到 5 之间。
stringArrayWrappersType
Type: string Default: variable
⚠️ 必须启用 stringArray 和 stringArrayWrappersCount 选项
允许选择由 stringArrayWrappersCount 选项追加的包装器类型。
可用值:
'variable':在每个作用域顶部追加变量包装器。性能较快。'function':在每个作用域内的随机位置追加函数包装器。性能比variable慢,但提供更严格的混淆。
当性能损失对被混淆应用影响不大时,强烈建议使用 function 包装器以获得更高的混淆强度。
'function' 选项值的示例:
// input
const foo = 'foo';
function test () {
const bar = 'bar';
console.log(foo, bar);
}
test();
// output
const a = [
'log',
'bar',
'foo'
];
const foo = d(0x567, 0x568);
function b(c, d) {
b = function (e, f) {
e = e - 0x185;
let g = a[e];
return g;
};
return b(c, d);
}
function test() {
const c = e(0x51c, 0x51b);
function e (c, g) {
return b(c - 0x396, g);
}
console[f(0x51b, 0x51d)](foo, c);
function f (c, g) {
return b(c - 0x396, g);
}
}
function d (c, g) {
return b(g - 0x3e1, c);
}
test();
stringArrayThreshold
Type: number Default: 0.75 Min: 0 Max: 1
⚠️ 必须启用 stringArray 选项
您可以用此设置来调整字符串字面量被放入 stringArray 的概率(从 0 到 1)。
对于代码体量较大的情况,此设置尤其有用,因为它会频繁调用 string array,可能拖慢代码运行速度。
stringArrayThreshold: 0 等价于 stringArray: false。
strictMode
Type: boolean | null Default: null
允许指定混淆器应如何处理与 JavaScript 严格模式相关的代码。
可用值:
null(默认)- 从代码中自动检测严格模式。如果代码含有显式的'use strict'指令、ES 模块语法或类方法,则按严格模式处理。否则按宽松(sloppy)模式处理。true- 对所有代码强制按严格模式处理,即使没有显式的'use strict'指令。当您的代码将在严格模式上下文中运行时(例如在 ES 模块、打包工具或现代框架中)使用此项。false- 只有显式的严格模式标志('use strict'、ES 模块、类方法)才按严格模式处理。父作用域的继承仍会依照 JS 规范生效。
target
Type: string Default: browser
允许为混淆后的代码设置目标环境。
可用值:
browser(默认)- 标准网页环境。输出代码与node相同,但某些浏览器专用选项不允许与node目标一起使用browser-no-eval- 与browser相同,但输出不使用eval()。当目标页面的内容安全策略(CSP)禁止eval/unsafe-eval时使用。node- Node.js 环境。浏览器专用选项会被禁用(它们需要window/document,在 Node 中要么无效要么会抛出异常)。某些依赖浏览器专有 API 的vmSelfDefending防御措施 - 无头浏览器检测、基于 iframe 的干净 realm 恢复、反检查器/DOM 检查 - 不会为该目标生成。service-worker- Service Worker 上下文。没有window,没有document,self全局对象也不同。userscript- 用户脚本管理器沙箱(如 Tampermonkey)。vmSelfDefending防御措施会相应调整。bytenode- 在混淆之后将用 bytenode 加载器编译(生成 V8 缓存字节码.jsc)的 Node.js 代码。混淆器本身不会调用bytenode;它输出的是经过 VM 混淆的 JavaScript,其运行时结构经过设计以承受 bytenode 的编译步骤,vmSelfDefending防御措施也会相应调整。您需要自行对混淆后的输出运行bytenode来生成最终的.jsc。
transformObjectKeys
Type: boolean Default: false
启用对对象键的转换。
示例:
// input
(function(){
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];
return _0x2275f8;
};
return _0x390c(_0x33d6b6, _0x4735f4);
}
(function () {
var _0x17d1b7 = _0x390c;
var _0xc9b6bb = {};
_0xc9b6bb[_0x17d1b7(0x199)] = _0x17d1b7(0x19c);
var _0x3d959a = {};
_0x3d959a[_0x17d1b7(0x198)] = _0x17d1b7(0x19b);
_0x3d959a[_0x17d1b7(0x19a)] = _0xc9b6bb;
var _0x41fd86 = _0x3d959a;
}());
warnings
Type: string | object Default: all
控制会发出哪些非致命的混淆警告。
可用值:
'all'(默认)- 发出每一条警告。'none'- 抑制所有警告。- 一个将警告类型映射为布尔值的对象 - 被映射为
false的类型会被抑制;任何未出现(或被映射为true)的类型都保持启用。例如,{ "VMGlobalFunctionNamesNotRenamed": false }会保留除该项之外的每一条警告。
警告类型:
VMGlobalFunctionNamesNotRenamed- 在vmObfuscation下,顶层函数声明、类声明以及被赋予函数/箭头函数/类表达式的变量,其名称被原样保留(renameGlobals选项被禁用且代码未被包裹在 IIFE 中),因此即便函数体已被隐藏为字节码,这些名称在输出中仍可读。导出的名称不在报告之列。VMTopLevelInitializerNotVirtualized- 由于vmWrapTopLevelInitializers被禁用或无法将其虚拟化,顶层变量初始化器在 VM 混淆下仍以普通 JavaScript 形式保留。DynamicCodeRenameRisk- 代码在运行时从字符串构建函数(直接eval、Function构造器,或注入到<script>/Worker 中的fn.toString()),这可能会引用被混淆器重命名过的标识符。VMDynamicCodeSkipped- 某个函数因包含直接eval/ 动态new Function/Function而被跳过 VM 字节码编译(参见vmForceCompileDynamicCode)。VMNoFunctionsToVirtualize- 已启用vmObfuscation,但代码中没有任何可虚拟化的函数(例如alert(1);这样只有顶层语句的代码),因此未应用任何 VM 保护。VM 混淆只保护函数体;请将要保护的代码包裹在函数中。VMSyncFunctionSkippedInAsyncMode- 在启用vmAsyncExecutor时,您在comment模式下显式标记的某个函数结果是同步函数,因而被跳过(该模式下只有异步函数会被虚拟化)。VMAsyncGeneratorSkippedInAsyncMode- 在启用vmAsyncExecutor且异步密钥获取器处于生效状态时,某个被标记的异步生成器无法被虚拟化(它必须同步返回其迭代器)。BrowserTargetWithNodeStyleCode- 代码看起来以 Node.js 为目标(例如require('fs')、__dirname、process.argv),而target选项却被设为类浏览器环境。ParseHtmlNoMarkedScripts- 已启用parseHtml,但输入中没有带data-javascript-obfuscator属性的<script>标签,因此没有任何内容被混淆,HTML 被原样返回。
vmObfuscation
Type: boolean Default: false
启用基于 VM 的字节码混淆。启用后,JavaScript 函数会被编译成运行在内嵌虚拟机上的自定义字节码。由于原始代码逻辑被彻底转换,这提供了最高级别的保护。
示例:
像 return qty * price 这样可读的代码会变成类似 [0x15,0x03,0x17,...] 的一串数字,只有内嵌的 VM 解释器才能执行它。原始逻辑不再以 JavaScript 形式可见。
vmTargetFunctions
Type: string[] Default: []
按名称精确指定哪些根级函数应获得 VM 保护。
示例:
{
vmObfuscation: true,
vmTargetFunctions: ['someFunctionName']
}
结果: 只有这三个函数会获得 VM 保护。其余一切都保持为普通(但仍经过混淆)的 JavaScript。非常适合保护敏感的许可证校验或身份验证逻辑,同时让代码的其余部分保持精简。
vmExcludeFunctions
Type: string[] Default: []
指定绝不应获得 VM 保护的根级函数。其优先级高于其他设置。
示例:
{
vmObfuscation: true,
vmExcludeFunctions: ['someFunctionName']
}
何时使用: 可以排除对性能要求高的根级函数(动画循环、实时数据处理),以避免 VM 开销,同时仍保护其余一切。
vmTargetFunctionsMode
Type: string Default: root
控制如何为 VM 混淆选择函数/方法。
示例 - comment 模式:
// Source code
function regularFunction() {
return 'not virtualized';
}
/* javascript-obfuscator:vm */
function sensitiveFunction() {
return 'this will be VM-protected';
}
function outer() {
/* javascript-obfuscator:vm */
function nestedSensitive() {
return 'nested but still VM-protected';
}
return nestedSensitive();
}
// Obfuscator options
{
vmObfuscation: true,
vmTargetFunctionsMode: 'comment'
}
何时使用: 当您需要精确控制究竟哪些函数获得 VM 保护时,尤其是那些包含敏感逻辑的嵌套函数。与只能作用于根级具名函数的 vmTargetFunctions 不同,comment 模式让您可以保护代码中任意位置的任意函数。
vmForceCompileDynamicCode
Type: boolean Default: false
控制 VM 混淆如何处理包含直接 eval、new Function(...) 或 Function(...) 调用的函数。
默认情况下,这样的函数(以及在其内部定义的每个函数)都会被跳过 VM 字节码编译,并报告一条 VMDynamicCodeSkipped 警告。这是因为运行时构建的源码可能引用其所处作用域链中的标识符 - 而这些标识符已被混淆器重命名。
当设为 true 时,该函数仍会被编译为字节码,且不再发出 VMDynamicCodeSkipped 警告。
无论此选项如何设置,另一条独立的 DynamicCodeRenameRisk 警告都会照常触发,因为它所描述的重命名风险与 VM 跳过无关 - 打开此选项并不会让底层的这种写法变得更安全。
// Source code
function loadConfig(src) {
return eval(src);
}
loadConfig('1 + 2');
// Options
{
vmObfuscation: true,
vmForceCompileDynamicCode: true
}
当选项关闭(默认)时,loadConfig 会保持为普通 JavaScript。当选项打开时,loadConfig 会像其他任何函数一样被编译为 VM 字节码。当您已审查过调用点,并确知运行时构建的代码不依赖被闭包重命名的标识符时,再使用此选项。
vmWrapTopLevelInitializers
Type: boolean Default: false
将部分顶层变量初始化器包裹在 IIFE(立即执行函数表达式)中,以便它们可以被 VM 混淆。
它的作用: 不使用此选项时,顶层常量和变量会在输出中保持可见:
// Input
const MY_STRING = "my-string";
// Output (without vmWrapTopLevelInitializers)
const MY_STRING = "my-string"; // String is visible!
启用此选项后,初始化器会被包裹在一个会被 VM 混淆的 IIFE 中:
// Input
const MY_STRING = "my-string";
// Output (with vmWrapTopLevelInitializers: true)
const MY_STRING = (() => { return /* VM bytecode call */ })(); // String hidden in bytecode
注意: 此选项仅在 vmTargetFunctionsMode 为 'root'(默认值)时生效。
警告: 每当有顶层初始化器在 VM 混淆下最终以普通 JavaScript 形式保留时,都会报告一条列出受影响变量名的 VMTopLevelInitializerNotVirtualized 警告。这涵盖以下情形:此选项被禁用;此选项不得不跳过的初始化器(并各自附带原因 - 例如初始化器引用了同级声明符,或包含顶层 await);以及 vmAsyncExecutor 模式下同步包装器根本无法被虚拟化的情况。
vmDynamicOpcodes
Type: boolean Default: false
让 VM 解释器更小,并且每次构建都各不相同。
它的作用:
- 过滤未使用的指令 - 如果您的代码不使用类,与类相关的指令会被完全移除
- 随机化结构 - 指令处理器的顺序会在每次构建时被打乱
其结果是:输出更小,且每次构建看起来都不一样。
vmBytecodeEncoding
Type: boolean Default: false
对每条字节码指令进行编码(在执行过程中逐条解码),并遮蔽存储在字节码常量池中的字符串常量,使编译后的字节码中不再留有明文字符串。
vmBytecodeArrayEncoding
Type: boolean Default: false
将整个字节码数组作为一个整体块进行编码。该数组在执行开始前于启动时一次性解码。与 vmBytecodeEncoding 一起使用可获得双层保护。
vmBytecodeArrayEncodingKey
Type: string Default: ''
用于字节码数组编码的自定义加密密钥。设置后,将使用此密钥代替默认的、由环境派生的密钥。该密钥必须在运行时通过 vmBytecodeArrayEncodingKeyGetter 提供。
此选项将加密密钥外部化 - 密钥不会内嵌在混淆后的代码本身中。虽然密钥在运行时仍可访问(因此并非真正保密),但这种分离可防止静态分析工具仅通过检查代码就找到密钥。
重要: 当混淆后的代码加载时,密钥必须能同步获取。请使用同步的存储方式,如 cookies、localStorage、sessionStorage、全局变量或 DOM 元素(例如服务端注入的 meta 标签)。像 fetch() 这样的异步方法不能直接用在密钥获取器表达式中。
vmBytecodeArrayEncodingKeyGetter
Type: string Default: ''
在运行时返回加密密钥的同步 JavaScript 表达式。该表达式会在混淆后的代码加载时求值,并且必须返回与 vmBytecodeArrayEncodingKey 中所提供的相同的密钥。若要异步解析密钥(返回一个 Promise),请启用 vmAsyncExecutor。
注意: 返回 Promise 的获取器需要 vmAsyncExecutor。这一点无法在构建时检查,因此在 vmAsyncExecutor 关闭时使用返回 Promise 的获取器会在运行时失败 - 解码器收到的是 Promise 而不是密钥。
只有当密钥 getter 返回与混淆时所用完全相同的密钥时,混淆后的代码才能正常工作。 如果密钥不匹配 - 或者 getter 返回 undefined、null 或空字符串 - 解密会产生错误的密钥流,代码将在运行时失败,输出乱码或抛出普通的运行时错误。这里有意不提供针对密钥的专门错误消息,因此失败的密钥与任何其他运行时故障无法区分。
重要: 不要把密钥和混淆后的代码放在同一个文件/脚本中 - 内联在那里会让哪怕是对打包产物纯粹的静态扫描也能恢复出它。请将其存放在单独的来源中:服务端设置的 cookies、由另一个脚本填充的 localStorage、服务端注入的 HTML meta 标签、由另一个脚本设置的全局变量,或(配合 vmAsyncExecutor)在运行时从您的后端获取。
当密钥从您的后端获取时(通过 vmAsyncExecutor),请在该端点上加入基于会话或来源的检查:向真实用户(有效会话、预期的 Origin/Referer)返回正确的密钥,向可疑请求(例如 localhost/非预期来源、无会话)返回垃圾密钥。真实用户正常运行;而在您的环境之外运行的副本拿到的密钥解密后什么也得不到。具体逻辑取决于您的站点。
示例:
// From cookie
vmBytecodeArrayEncodingKeyGetter: "document.cookie.match(/vmKey=([^;]+)/)?.[1]"
// From localStorage
vmBytecodeArrayEncodingKeyGetter: "localStorage.getItem('vmKey')"
// From global variable
vmBytecodeArrayEncodingKeyGetter: "window.__VM_KEY__"
// From meta tag (server-injected)
vmBytecodeArrayEncodingKeyGetter: "document.querySelector('meta[name=\"vm-key\"]').content"
// From nested object
vmBytecodeArrayEncodingKeyGetter: "window.config.encryption.key"
// From backend, async (requires vmAsyncExecutor)
vmBytecodeArrayEncodingKeyGetter: 'fetch("/vm-key").then((res) => res.text())'
用法示例:
// Build time
JavaScriptObfuscator.obfuscate(code, {
vmObfuscation: true,
vmBytecodeArrayEncoding: true,
vmBytecodeArrayEncodingKey: 'mySecretKey123',
vmBytecodeArrayEncodingKeyGetter: 'window.__VM_KEY__'
});
// Runtime - key must be set before obfuscated code runs
window.__VM_KEY__ = 'mySecretKey123';
vmAsyncExecutor
Type: boolean Default: false
启用异步 VM 执行器,它让 vmBytecodeArrayEncodingKeyGetter 可以返回一个 Promise(即异步密钥获取器)- 这样解密密钥就可以在运行时获取(网络请求、IndexedDB 等),而不必在代码加载时同步可用。
**强烈推荐用于完全异步的代码库。**在此模式下只有 async 函数会被虚拟化 - 同步函数无法在不把返回值变成 Promise、进而破坏其调用方的前提下改成异步 - 因此全程 async 的代码能获得最多的覆盖。当根部是同步的(例如同步的 IIFE / UMD 包装器)时它仍然有效:其内部最外层的 async 函数会被保护,而同步部分则保持原样。
哪些会被转换:每一个最外层的 async 函数,无论它出现在何处(包括嵌套在同步包装器内部)。每条链中最外层的 async 函数就是被保护的单元 - 它内部的一切,无论同步还是异步,都会被编译进去。同步函数和普通生成器则保持不被混淆。
function foo() { // sync — left as-is
function bar() {} // sync — left as-is
async function baz() { // transformed
// any code here, including calls to other async or sync functions
}
async function bark() { // transformed
// any code here, including calls to other async or sync functions
}
}
**跳过与警告。**当异步密钥获取器处于生效状态时,异步生成器也会保持不被混淆(异步生成器必须同步返回其迭代器,无法等待密钥)。在默认的 vmTargetFunctionsMode: 'root' 下,跳过是静默的(选择是自动的);在 comment 模式下,每当您显式标记的某个函数无法被虚拟化时 - 它结果是同步的,或者它是异步密钥获取器下的异步生成器 - 都会发出一条警告。
异步密钥获取器还额外需要启用 vmBytecodeArrayEncoding 并配备 vmBytecodeArrayEncodingKeyGetter。
用法示例:
JavaScriptObfuscator.obfuscate(code, {
vmObfuscation: true,
vmAsyncExecutor: true,
vmBytecodeArrayEncoding: true,
vmBytecodeArrayEncodingKey: 'mySecretKey123',
// the key getter may now return a Promise
vmBytecodeArrayEncodingKeyGetter: 'fetch("/vm-key").then((res) => res.text())'
});
vmJumpsEncoding
Type: boolean Default: false
对字节码中的跳转目标进行编码。跳转偏移量在运行时计算,从而对静态分析隐藏控制流结构(if/else、循环等)。
vmMacroOps
Type: boolean Default: false
将常见的指令序列合并为单个「宏」操作码。例如,LOAD_ARG + PUSH_CONST + SUB 可以变成 MACRO_SUB_ARG_CONST,从而减少解释器分派。它对默认的栈式 VM 和 vmRegisterBased: true 均有效;在任一模式下都需显式启用 vmMacroOps: true。
vmDebugProtection
Type: boolean | object Default: false
为 VM 运行时添加多层反调试、反分析和反 LLM 防御。在 browser/browser-no-eval 目标下效果最佳。
传入 true 以启用,或传入 false 以禁用。传入一个对象可在关闭某项特定防御的同时启用它:
{
vmDebugProtection: {
// most defenses are always on; but CDP/devtools detection is disabled
inspectorDetection: false
}
}
⚠️ 该对象并非可供开启的防御菜单。 当调试保护启用时,其绝大多数防御始终处于开启状态且无法关闭。下面的子选项仅暴露少数一些用户可能有意需要放宽的防御(例如,因为误报会破坏正当的工作流) - 其余每一项防御都会照常保持开启。
💡 对象形式可通过 API 使用
⚠️ 自动化框架。 在 inspectorDetection 开启时(默认),使用基于 CDP 的工具(Puppeteer、Playwright、Selenium/ChromeDriver)驱动受保护页面会被检测为已附加的检查器。如果您要对受保护代码运行自动化测试,请以 vmDebugProtection: { inspectorDetection: false } 来构建这些版本。
vmSelfDefending
Type: boolean Default: false
为 VM 运行时添加多层次的篡改检测、反 Hook 和反逆向工程保护。
⚠️ 该选项会强制启用 vmBytecodeArrayEncoding。
⚠️ 敏感环境检测。该选项会将混淆后的代码绑定到其目标运行时环境,并使用高级浏览器指纹识别来检测自动化工具。使用该选项保护的代码在以下环境中运行时会有意地失效:
- 无头浏览器(无头 Chrome/Chromium、PhantomJS)
- 浏览器自动化工具(Puppeteer、Playwright、Cypress、Selenium/ChromeDriver、Nightmare)
- Node.js(当
target设为browser时) - jsdom 或类似的服务端 DOM 模拟
- 原生浏览器内置函数被 Hook 或替换的环境
该代码在常规浏览器(Chrome、Firefox、Safari、Edge)中能够正常工作,包括在 iframe、浏览器扩展(内容脚本)和 Web Worker 中加载时。如果您需要对受保护的代码运行自动化测试,请在测试构建中禁用 vmSelfDefending - 该选项旨在阻止自动化分析,无法与任何自动化框架安全地一起使用。
强烈建议与 vmDebugProtection、vmBytecodeArrayEncodingKey 和 vmBytecodeArrayEncodingKeyGetter 一起使用。
vmDefenseHook
Type: { name: string, aliases?: object } | null Default: null
vmDefenseHook 为 null(禁用)或一个具有两个键的对象:name(必填)和 aliases(可选)。
name 是您的宿主页面定义的全局函数,当 VM 防御(vmDebugProtection / vmSelfDefending)检测到敌对信号时 - 调试器或检查器、无头/自动化浏览器、AI 编码代理进程、不被允许的域名等等 - 会带着一个信号对象调用它。用它把该事件上报到您的后端(例如 navigator.sendBeacon)。该 hook 是一个纯遥测汇聚点:它的返回值会被忽略,缺失或抛出异常的 hook 只是一个静默的空操作,绝不会禁用任何防御。要改变某项防御在检测到时所做的事,请使用 vmDefenseReaction。
aliases 可选地重命名该信号对象的字段 - 详见下文重命名信号字段。
**信号对象。**该 hook 会收到单个 signal:
source- 触发的具体检测器(见下表)。category- 它所归属的类别:automation(非人类浏览器)、debugger(有调试器/检查器处于活动状态)、sandbox(被插桩的/伪造的宿主)、domain(域名锁定违规)、tamper(内置函数在运行时被打了补丁)或integrity(VM 自身的代码被篡改)。score/threshold- 检测器触发的强度,以及它需要达到的值;只有当score >= threshold时 hook 才会触发。大多数检查是全有或全无的(单个决定性信号);headless会对多个浏览器形态信号求和,因此它的score通常高于其threshold。
注册 hook。请在混淆后的包加载之前将它定义为一个普通的全局变量 - VM 运行时及其防御会先于您(受保护)的程序运行,因此许多检测会在启动期间触发:
// in your page, before the obfuscated script:
window.__vmDetection = function (signal) { navigator.sendBeacon('/vm-defense', JSON.stringify(signal)); };
// obfuscation option:
vmDefenseHook: { name: '__vmDetection' }
定义在混淆源码内部的 hook 注册得太晚,无法捕捉启动期的检测;而且如果它被 VM 编译,则在您的程序运行之前都无法被触及。无论哪种方式它都是安全的(缺失的 hook 会空操作,重入保护也能防止任何失控),但为了完整覆盖,请提前注册它。若还想保护您的上报逻辑,可让注册的 hook 只是一行缓冲((window.__vmDet = window.__vmDet || []).push(signal)),再从您的混淆代码中读取并发送该缓冲。
**重命名信号字段(aliases)。**默认的 source/category 值都是描述性名称,因此任何对该回调进行插桩(或阅读输出)的人都能识别出这项保护以及是哪个检测器触发的。aliases 会把信号字段重命名为您自选的不透明标记,并在 VM 内部、于信号发出之前应用,因此这些名称绝不会出现在输出中,也不会到达回调。您的应用知道自己的映射,并把这些标记转发到您的后端。
别名是按字段设置的,将键名与取值的重命名分开处理:每个字段接受一个 key(回调所收到的属性名);字符串型的名称字段 source 和 category 还接受一个 values 映射,而 score/threshold 是数字,只接受 key。可映射的名称如下(其他任何名称都会在构建时被拒绝):
- 字段键名 -
source、category、score、threshold source取值 -headless、agent、agentBrowser、node、debugger、timing、sandbox、domain、nativeHook、integritycategory取值 -automation、debugger、sandbox、domain、tamper、integrity
vmDefenseHook: {
name: '__vmDetection',
aliases: {
source: { key: 'a8Qm', values: { headless: 'xP4m9Q' } },
category: { key: 'p3Tx', values: { automation: 'bQ7s1M' } },
score: { key: 's1' },
threshold: { key: 't1' }
}
// the callback now receives e.g. { a8Qm: 'xP4m9Q', p3Tx: 'bQ7s1M', s1: <score>, t1: <threshold> }
}
这是指纹规避,而非保密 - 该映射仍可通过反复测试推断出来 - 因此它唯一的好处是不暴露稳定、不言自明的名称。未设置的条目保留其默认名称。
vmDefenseReaction
Type: object Default: { automation: 'break', debugger: 'decoy', sandbox: 'decoy', domain: 'break', tamper: 'break', integrity: 'break' }
配置每个检测类别如何响应。它不会启用任何东西 - 防御本身由 vmSelfDefending、vmDebugProtection 和 vmDomainLock 开启;该选项只选择已启用的防御如何响应。类别是控制的单位 - 一个类别中的每个检测器都执行该类别的响应。
每个类别都归集了监视某一类敌对状况的检测器。只有当发出其检测器的那个选项被启用时,该类别才会响应:
每个类别都对应 vmSelfDefending、vmDebugProtection 和 vmDomainLock 中的一个或多个;不存在这三个选项之外的类别,而为某个其选项处于关闭状态的类别所设置的响应根本不会生效。
键是这六个类别名称,或者 default(未指定类别的后备)。值为:
break- 立即中断decoy- 在被污染的状态上继续运行,静默地产生错误结果none- 本地什么都不做(仅遥测)
每个类别的默认值如上所示;您未设置(或设为其默认值)的类别会使用该默认值。default 会作用于每一个类别,包括那些天生正确的类别(integrity、tamper),因此 { default: 'none' } 是一个真正不中断、仅遥测的构建:
vmDefenseReaction: { default: 'none' } // never break — pair with vmDefenseHook
vmDefenseReaction: { automation: 'none', domain: 'break' } // tolerate automation FPs, still break on a bad domain
browserEnvironment
Type: object Default: {}
声明生产环境构建产物所运行环境的事实,使受保护的代码能够将自身绑定到这些事实上、据此作出反应或容忍它们。仅适用于 browser / browser-no-eval / service-worker 目标 - 对于 node、userscript 和 bytenode 会被拒绝。每个字段都与下文注明的某一特定防护配合生效。
字段:
transport- 生产环境提供该 bundle 所用的协议:'http'或'https'。设为'https'时,构建产物会把自身完整性与「通过 HTTPS 提供」绑定,因此分析者拷走后用纯 HTTP 提供的副本(本地逆向工程的常见做法)将无法正确运行。设为'http'或不设置该字段则不会添加任何绑定。与vmSelfDefending配合时生效。
browserEnvironment: { transport: 'https' }
hosting- 生产环境 bundle 的托管来源:'remote'或'local'。设为'remote'时,构建产物会把自身完整性与「从远程主机提供」绑定,因此分析者拷走后在自己本地环境中运行的副本会被视为运行时环境不匹配,自动化防御会作出反应(参见vmDebugProtection和vmDefenseReaction)。设为'local'或不设置该字段则不会添加任何绑定。与vmDebugProtection配合时生效,且仅限browser/browser-no-eval。
browserEnvironment: { transport: 'https', hosting: 'remote' }
hookedBuiltins- 设为true,声明您的生产构建所运行的运行时会合法地用 JavaScript 包装函数替换原生内置对象:应用自身的防篡改机制、宿主页面,或共享同一 realm 的其他浏览器扩展。通常情况下,vmSelfDefending会把被替换的原生内置对象视为篡改并阻止构建运行;设置此字段后,它会容忍这样的环境,代码照常运行。false或未设置该字段则保持严格行为。与vmSelfDefending配合时生效。
browserEnvironment: { hookedBuiltins: true }
此选项仅放宽原生性检查;干净 realm 校验和必需的内建行为仍然强制执行。
⚠️ hookedBuiltins 会有意放宽篡改检测:一旦设置,分析者为了检查您的代码而包装这些相同的内置对象时也不会再被阻止。VM 虚拟化、反调试和完整性保护不受影响。仅当已知您的生产运行时会 Hook 内置对象、并且可以接受这种较弱的保证时才启用它。
⚠️ transport 和 hosting 字段会把受保护的构建产物绑定到您所声明的环境。同一构建产物在任何不匹配的环境中加载 - 包括在到达最终环境之前的短暂过渡阶段 - 都会有意地无法正确运行。只有在加载生产环境构建产物的每个上下文都与之匹配时才声明某个字段,并且不要把这些声明用于本地开发、测试和 CI 所用的构建产物。
vmStatefulOpcodes
Type: boolean Default: false
让操作码的含义取决于其在字节码中的位置。每个位置都有一套由种子派生出的不同的操作码到处理器的映射,因此同一个操作码编号在不同位置会执行不同的操作。
vmCallContextOpcodes
Type: boolean Default: false
让受保护的函数依赖于它被从何处调用,这样它就无法被从代码中抽离出来单独运行或分析 - 只有通过程序中真实的调用点被调用时,它才会正确工作。该选项会影响运行时性能。
目前仅支持以下几种结构:
- 函数声明(
function f() {}); - 赋值给变量的函数表达式和箭头函数(
const f = () => {}); - 实例私有方法(
this.#m())。
在所有情况下,该函数都必须始终通过直接调用(f()、this.#m())来触及。如果它被存入另一个变量、作为参数传递,或以其他方式当作值使用,则不会受到保护。支持异步函数;不支持生成器。
该选项是实验性的,可能破坏您的代码,因此在使用前请充分测试输出。
vmStackEncoding
Type: boolean Default: false
在执行期间对 VM 栈上的值进行加密。值在入栈时被编码,出栈时被解码,因此内存检查看到的是加密数据而非实际值。
该选项会严重影响性能。
vmCompactDispatcher
Type: boolean Default: false
使用单个 VM 执行器,而非双执行器(同步 + 生成器)。可减小混淆后代码的体积,但会为递归密集的代码增加约 20% 的性能开销。
false(默认):双执行器 - 性能最优,输出较大true:单执行器 - 输出更小,略慢
vmRegisterBased
Type: boolean Default: false
将 VM 从默认的基于栈的字节码切换为基于寄存器的执行模型,在某些情况下可将 VM 的运行时性能提升约 15-20%,但会使混淆后的代码体积略有增加。
由于它生成的字节码与执行器在结构上不同,因此也会让 VM 具有比默认基于栈的实现更为独特的指纹 - 当您希望改变 VM 的形态、使其更难被通用分析识别时,可以启用该选项。
在底层,这并不是一个原生的基于寄存器的编译器 - 字节码仍由常规的基于栈的编译器生成,随后由一个独立的转换阶段将其改写为基于寄存器的形式。
该选项是实验性的 - 请测试在启用 vmRegisterBased 时您的代码能否正常运行。
vmStringArrayBytecodeOnly
Type: boolean Default: false
启用后,字符串数组将只从字节码数据中提取字符串 - 代码中的其他字符串都不会被转换。即便没有显式设置,这也会强制启用 stringArray。
**为何使用它:**将所有 VM 运行时字符串提取到字符串数组中很慢。该选项只针对字节码内容进行字符串数组提取,在仍然保护字节码常量的同时提升性能。
- 当
vmBytecodeArrayEncoding: false时 - 提取字节码常量池(c数组)中的字符串 - 当
vmBytecodeArrayEncoding: true时 - 提取顶层 base64 编码的字节码字符串 stringArrayThreshold仍然控制这些字节码字符串中有多大比例会被提取
vmDomainLock
Type: string[] Default: []
⚠️ 该选项不适用于 target: 'node'、target: 'service-worker' 或 target: 'bytenode'
将混淆后的代码限制在特定的域名和/或子域名上,并且比 domainLock 更难被定位和剥离。
如果源代码没有在该选项指定的域名上运行,浏览器将被重定向到传给 vmDomainLockRedirectUrl 的 URL,而且即使重定向被抑制,后续受保护的调用也会返回错误的结果。
多个域名和子域名
您可以将代码锁定到多个域名或子域名。例如,要将代码锁定为只在 www.example.com 上运行,添加 www.example.com。要让它在根域名及其任意子域名(example.com、sub.example.com)上生效,使用 .example.com。
vmDomainLockRedirectUrl
Type: string Default: about:blank
⚠️ 该选项不适用于 target: 'node'、target: 'service-worker' 或 target: 'bytenode'
如果源代码没有在 vmDomainLock 指定的域名上运行,允许将浏览器重定向到所传入的 URL。
预设选项
高强度混淆,低性能
性能会比不混淆时慢得多。
{
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1,
deadCodeInjection: true,
deadCodeInjectionThreshold: 1,
debugProtection: true,
debugProtectionInterval: 4000,
disableConsoleOutput: true,
identifierNamesGenerator: 'hexadecimal',
log: false,
numbersToExpressions: true,
renameGlobals: false,
selfDefending: true,
simplify: true,
splitStrings: true,
splitStringsChunkLength: 5,
strictMode: null,
stringArray: true,
stringArrayCallsTransform: true,
stringArrayEncoding: ['rc4'],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayWrappersCount: 5,
stringArrayWrappersChainedCalls: true,
stringArrayWrappersParametersMaxCount: 5,
stringArrayWrappersType: 'function',
stringArrayThreshold: 1,
transformObjectKeys: true
}
中等强度混淆,性能均衡
性能会比不混淆时慢一些。
{
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.75,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.4,
debugProtection: false,
debugProtectionInterval: 0,
disableConsoleOutput: true,
identifierNamesGenerator: 'hexadecimal',
log: false,
numbersToExpressions: true,
renameGlobals: false,
selfDefending: true,
simplify: true,
splitStrings: true,
splitStringsChunkLength: 10,
strictMode: null,
stringArray: true,
stringArrayCallsTransform: true,
stringArrayCallsTransformThreshold: 0.75,
stringArrayEncoding: ['base64'],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayWrappersCount: 2,
stringArrayWrappersChainedCalls: true,
stringArrayWrappersParametersMaxCount: 4,
stringArrayWrappersType: 'function',
stringArrayThreshold: 0.75,
transformObjectKeys: true
}
低强度混淆,高性能
性能会保持在相对正常的水平。
{
compact: true,
controlFlowFlattening: false,
deadCodeInjection: false,
debugProtection: false,
debugProtectionInterval: 0,
disableConsoleOutput: true,
identifierNamesGenerator: 'hexadecimal',
log: false,
numbersToExpressions: false,
renameGlobals: false,
selfDefending: true,
simplify: true,
splitStrings: false,
strictMode: null,
stringArray: true,
stringArrayCallsTransform: false,
stringArrayEncoding: [],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayWrappersCount: 1,
stringArrayWrappersChainedCalls: true,
stringArrayWrappersParametersMaxCount: 2,
stringArrayWrappersType: 'variable',
stringArrayThreshold: 0.75
}
默认预设,高性能
{
compact: true,
controlFlowFlattening: false,
deadCodeInjection: false,
debugProtection: false,
debugProtectionInterval: 0,
disableConsoleOutput: false,
identifierNamesGenerator: 'hexadecimal',
log: false,
numbersToExpressions: false,
renameGlobals: false,
selfDefending: false,
simplify: true,
splitStrings: false,
strictMode: null,
stringArray: true,
stringArrayCallsTransform: false,
stringArrayCallsTransformThreshold: 0.5,
stringArrayEncoding: [],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayWrappersCount: 1,
stringArrayWrappersChainedCalls: true,
stringArrayWrappersParametersMaxCount: 2,
stringArrayWrappersType: 'variable',
stringArrayThreshold: 0.75
}
VM 超高强度混淆(最高安全性)
该预设启用基于 VM 的字节码混淆,并包含所有强化特性,包括间接分发。提供最强的保护,但输出体积更大、执行速度慢得多。
{
optionsPreset: 'vm-ultra-high-obfuscation'
}
或者逐项配置:
{
compact: true,
simplify: true,
identifierNamesGenerator: 'mangled-shuffled',
vmObfuscation: true,
vmForceCompileDynamicCode: false,
vmWrapTopLevelInitializers: true,
vmDynamicOpcodes: true,
vmBytecodeEncoding: true,
vmBytecodeArrayEncoding: true,
vmBytecodeArrayEncodingKey: '',
vmBytecodeArrayEncodingKeyGetter: '',
vmAsyncExecutor: false,
vmJumpsEncoding: true,
vmMacroOps: true,
vmDebugProtection: true,
vmSelfDefending: true,
vmDefenseHook: null,
vmDefenseReaction: {
automation: 'break',
debugger: 'decoy',
sandbox: 'decoy',
domain: 'break',
tamper: 'break',
integrity: 'break'
},
vmStatefulOpcodes: true,
vmCallContextOpcodes: false,
vmStackEncoding: true,
vmCompactDispatcher: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.5,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.5,
debugProtection: true,
debugProtectionInterval: 4000,
disableConsoleOutput: true,
identifierNamesGenerator: 'hexadecimal',
log: false,
numbersToExpressions: true,
renameGlobals: false,
selfDefending: true,
simplify: true,
splitStrings: true,
splitStringsChunkLength: 5,
strictMode: null,
stringArray: true,
stringArrayCallsTransform: true,
stringArrayEncoding: ['rc4'],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayWrappersCount: 5,
stringArrayWrappersChainedCalls: true,
stringArrayWrappersParametersMaxCount: 5,
stringArrayWrappersType: 'function',
stringArrayThreshold: 0.5,
transformObjectKeys: true
}
VM 高强度混淆(最高安全性)
该预设启用基于 VM 的字节码混淆,并包含大多数强化特性。提供强力保护,且性能优于超高强度预设。
{
optionsPreset: 'vm-high-obfuscation'
}
或者逐项配置:
{
compact: true,
simplify: true,
identifierNamesGenerator: 'mangled-shuffled',
vmObfuscation: true,
vmForceCompileDynamicCode: false,
vmWrapTopLevelInitializers: true,
vmDynamicOpcodes: true,
vmBytecodeEncoding: true,
vmBytecodeArrayEncoding: true,
vmBytecodeArrayEncodingKey: '',
vmBytecodeArrayEncodingKeyGetter: '',
vmAsyncExecutor: false,
vmJumpsEncoding: true,
vmMacroOps: true,
vmDebugProtection: true,
vmSelfDefending: true,
vmDefenseHook: null,
vmDefenseReaction: {
automation: 'break',
debugger: 'decoy',
sandbox: 'decoy',
domain: 'break',
tamper: 'break',
integrity: 'break'
},
vmStatefulOpcodes: true,
vmCallContextOpcodes: false,
vmStackEncoding: true,
vmCompactDispatcher: false
}
VM 中等强度混淆(安全性均衡)
该预设启用基于 VM 的字节码混淆,并包含一组均衡的强化特性。在安全性与性能之间取得良好折中。
{
optionsPreset: 'vm-medium-obfuscation'
}
或者逐项配置:
{
compact: true,
simplify: true,
identifierNamesGenerator: 'mangled-shuffled',
vmObfuscation: true,
vmForceCompileDynamicCode: false,
vmWrapTopLevelInitializers: true,
vmDynamicOpcodes: true,
vmBytecodeEncoding: true,
vmBytecodeArrayEncoding: false,
vmBytecodeArrayEncodingKey: '',
vmBytecodeArrayEncodingKeyGetter: '',
vmAsyncExecutor: false,
vmJumpsEncoding: true,
vmMacroOps: true,
vmDebugProtection: true,
vmSelfDefending: false,
vmDefenseHook: null,
vmDefenseReaction: {
automation: 'break',
debugger: 'decoy',
sandbox: 'decoy',
domain: 'break',
tamper: 'break',
integrity: 'break'
},
browserEnvironment: {},
vmStatefulOpcodes: false,
vmCallContextOpcodes: false,
vmStackEncoding: false,
vmCompactDispatcher: false
}
VM 低强度混淆(基础安全性,更好性能)
该预设启用基础的基于 VM 的字节码混淆,不含额外的强化特性。在安全性与输出体积之间取得良好平衡。
{
optionsPreset: 'vm-low-obfuscation'
}
或者逐项配置:
{
compact: true,
simplify: true,
identifierNamesGenerator: 'mangled-shuffled',
vmObfuscation: true,
vmForceCompileDynamicCode: false,
vmWrapTopLevelInitializers: true,
vmDynamicOpcodes: false,
vmBytecodeEncoding: false,
vmBytecodeArrayEncoding: false,
vmBytecodeArrayEncodingKey: '',
vmBytecodeArrayEncodingKeyGetter: '',
vmAsyncExecutor: false,
vmJumpsEncoding: false,
vmMacroOps: false,
vmDebugProtection: false,
vmSelfDefending: false,
vmDefenseHook: null,
vmDefenseReaction: {
automation: 'break',
debugger: 'decoy',
sandbox: 'decoy',
domain: 'break',
tamper: 'break',
integrity: 'break'
},
browserEnvironment: {},
vmStatefulOpcodes: false,
vmCallContextOpcodes: false,
vmStackEncoding: false,
vmCompactDispatcher: false
}
VM 默认(VM + 字符串数组保护)
该预设将基础的基于 VM 的字节码混淆与字符串数组保护结合在一起。是启用带字符串保护的 VM 混淆的良好起点。
{
optionsPreset: 'vm-default'
}
或者逐项配置:
{
compact: true,
simplify: true,
identifierNamesGenerator: 'mangled-shuffled',
vmObfuscation: true,
vmForceCompileDynamicCode: false,
vmWrapTopLevelInitializers: true,
vmDynamicOpcodes: true,
vmBytecodeEncoding: true,
vmBytecodeArrayEncoding: true,
vmStringArrayBytecodeOnly: true,
vmAsyncExecutor: false,
vmJumpsEncoding: true,
vmMacroOps: true,
vmDebugProtection: {
inspectorDetection: true
},
vmSelfDefending: true,
vmDefenseHook: null,
vmDefenseReaction: {
automation: 'break',
debugger: 'decoy',
sandbox: 'decoy',
domain: 'break',
tamper: 'break',
integrity: 'break'
},
browserEnvironment: {},
vmStatefulOpcodes: false,
vmCallContextOpcodes: false,
vmStackEncoding: false,
vmCompactDispatcher: false,
stringArray: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayThreshold: 1,
stringArrayIndexShift: true,
stringArrayIndexesType: ['hexadecimal-number'],
stringArrayCallsTransform: true,
stringArrayCallsTransformThreshold: 1,
stringArrayWrappersCount: 3,
stringArrayWrappersType: 'function',
stringArrayWrappersParametersMaxCount: 5,
stringArrayWrappersChainedCalls: true,
stringArrayEncoding: ['base64'],
splitStrings: true,
splitStringsChunkLength: 6
}
