<!DOCTYPE html>
<html>
<head>
<title>专业版 JS 混淆解密工具</title>
<style>
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.alert {
padding: 10px 15px;
margin: 10px 0;
border-radius: 4px;
display: none;
}
.alert.error { background: #fee; color: #d32f2f; }
.alert.success { background: #efe; color: #2e7d32; }
.alert.show { display: block; }
.config-panel {
background: #f5f5f5;
padding: 15px;
border-radius: 4px;
margin-bottom: 15px;
}
.config-group {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.config-item { display: flex; align-items: center; gap: 5px; }
.buttons {
margin: 15px 0;
display: flex;
gap: 12px;
}
button {
padding: 10px 20px;
cursor: pointer;
border: none;
border-radius: 4px;
background: #2196f3;
color: white;
transition: background 0.3s;
}
button:hover { background: #0b7dda; }
button:disabled { background: #bbb; cursor: not-allowed; }
.code-container {
display: flex;
gap: 20px;
margin-top: 10px;
}
.code-box {
flex: 1;
display: flex;
flex-direction: column;
}
textarea {
width: 100%;
height: 400px;
padding: 12px;
font-family: 'Consolas', monospace;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
}
label { margin-bottom: 8px; font-weight: 500; }
.progress {
height: 4px;
background: #eee;
border-radius: 2px;
margin: 10px 0;
overflow: hidden;
display: none;
}
.progress.show { display: block; }
.progress-bar {
height: 100%;
background: #4caf50;
width: 0%;
transition: width 0.3s;
}
</style>
</head>
<body>
<div class="container">
<div class="alert" id="alert"></div>
<div class="config-panel">
<h4>混淆配置</h4>
<div class="config-group">
<div class="config-item">
<input type="checkbox" id="mangleVars" checked>
<label for="mangleVars">混淆变量名</label>
</div>
<div class="config-item">
<input type="checkbox" id="mangleFuncs" checked>
<label for="mangleFuncs">混淆函数名</label>
</div>
<div class="config-item">
<input type="checkbox" id="mangleClasses" checked>
<label for="mangleClasses">混淆类名</label>
</div>
<div class="config-item">
<input type="checkbox" id="flattenControl" checked>
<label for="flattenControl">控制流扁平化</label>
</div>
</div>
</div>
<div class="buttons">
<button onclick="handleObfuscate()" id="obfBtn">混淆代码</button>
<button onclick="handleDeobfuscate()" id="deobfBtn">解密代码</button>
<button onclick="copyResult()">复制结果</button>
</div>
<div class="progress">
<div class="progress-bar" id="progressBar"></div>
</div>
<div class="code-container">
<div class="code-box">
<label for="inputCode">输入代码:</label>
<textarea id="inputCode" placeholder="输入要处理的JS代码...">function outer() {
let a = 1;
function inner() {
let a = 2;
console.log(a);
}
inner();
class MyClass {
constructor() { this.value = a; }
}
return new MyClass();
}</textarea>
</div>
<div class="code-box">
<label for="outputCode">处理结果:</label>
<textarea id="outputCode" placeholder="处理结果将显示在这里..." spellcheck="false"></textarea>
</div>
</div>
</div>
<script>
// 工具函数:非阻塞提示
const showAlert = (message, isError = true) => {
const alertEl = document.getElementById('alert');
alertEl.textContent = message;
alertEl.className = `alert ${isError ? 'error' : 'success'} show`;
setTimeout(() => alertEl.classList.remove('show'), 3000);
};
// 进度更新
const updateProgress = (percent) => {
const progress = document.querySelector('.progress');
const bar = document.getElementById('progressBar');
progress.classList.add('show');
bar.style.width = `${Math.min(100, percent)}%`;
if (percent >= 100) {
setTimeout(() => progress.classList.remove('show'), 500);
}
};
// 动态密钥生成
const getDynamicKey = () => {
return Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
};
// 兼容IE的展开语法替代
const fromCharCode = (arr) => {
if (Array.isArray(arr) && 'from' in String) {
return String.fromCharCode(...arr);
}
// IE兼容:手动拼接
let str = '';
for (let i = 0; i < arr.length; i++) {
str += String.fromCharCode(arr[i]);
}
return str;
};
// 加密解密核心(使用动态密钥+Web Crypto降级)
const cryptoUtil = {
// 检测Web Crypto支持
supportsWebCrypto: () => typeof window !== 'undefined' && 'crypto' in window && 'subtle' in window.crypto,
// 异或加密(降级方案)
xorEncrypt: (str, key) => {
const encrypted = [];
for (let i = 0; i < str.length; i++) {
const keyChar = key.charCodeAt(i % key.length);
encrypted.push(str.charCodeAt(i) ^ keyChar);
}
return btoa(fromCharCode(encrypted));
},
// 异或解密(降级方案)
xorDecrypt: (str, key) => {
const decoded = atob(str);
const decrypted = [];
for (let i = 0; i < decoded.length; i++) {
const keyChar = key.charCodeAt(i % key.length);
decrypted.push(decoded.charCodeAt(i) ^ keyChar);
}
return fromCharCode(decrypted);
},
// 加密入口(自动选择方案)
encrypt: async (str) => {
const key = getDynamicKey();
if (this.supportsWebCrypto()) {
// Web Crypto加密(更安全)
const encoder = new TextEncoder();
const keyMaterial = await window.crypto.subtle.importKey(
'raw',
encoder.encode(key),
{ name: 'AES-GCM' },
false,
['encrypt']
);
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const encrypted = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
keyMaterial,
encoder.encode(str)
);
return {
data: btoa(fromCharCode([...new Uint8Array(encrypted)])),
key,
iv: btoa(fromCharCode([...iv])),
method: 'aes'
};
} else {
// 降级为异或加密
return {
data: this.xorEncrypt(str, key),
key,
method: 'xor'
};
}
},
// 解密入口
decrypt: async (payload) => {
if (payload.method === 'aes' && this.supportsWebCrypto()) {
const decoder = new TextDecoder();
const keyMaterial = await window.crypto.subtle.importKey(
'raw',
new TextEncoder().encode(payload.key),
{ name: 'AES-GCM' },
false,
['decrypt']
);
const iv = new Uint8Array([...atob(payload.iv)].map(c => c.charCodeAt(0)));
const encrypted = new Uint8Array([...atob(payload.data)].map(c => c.charCodeAt(0)));
const decrypted = await window.crypto.subtle.decrypt(
{ name: 'AES-GCM', iv },
keyMaterial,
encrypted
);
return decoder.decode(decrypted);
} else {
// 异或解密
return this.xorDecrypt(payload.data, payload.key);
}
}
};
// 变量名生成器(避免关键字)
const keywordSet = new Set([
'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default',
'delete', 'do', 'else', 'export', 'extends', 'finally', 'for', 'function',
'if', 'import', 'in', 'instanceof', 'new', 'return', 'super', 'switch',
'this', 'throw', 'try', 'typeof', 'var', 'let', 'void', 'while', 'with',
'yield', 'enum', 'implements', 'interface', 'package', 'protected', 'static'
]);
const generateVarName = (index) => {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const char = chars[index % chars.length];
const num = Math.floor(index / chars.length);
const name = `_${char}${num > 0 ? num : ''}`;
return keywordSet.has(name) ? generateVarName(index + 1) : name;
};
// 作用域解析器(简单实现嵌套作用域)
class ScopeAnalyzer {
constructor() {
this.scopes = []; // 作用域栈 [当前作用域, 父作用域, ...]
this.currentScope = new Map(); // 变量映射表: 原始名 -> 混淆名
this.varIndex = 0;
}
// 进入新作用域(如函数、类)
enterScope() {
this.scopes.push(this.currentScope);
this.currentScope = new Map();
}
// 退出当前作用域
exitScope() {
this.currentScope = this.scopes.pop() || new Map();
}
// 获取变量混淆名(同一作用域内同名变量复用)
getMangledName(originalName) {
if (this.currentScope.has(originalName)) {
return this.currentScope.get(originalName);
}
// 检查所有父作用域,避免变量名冲突
let isConfict = false;
for (const scope of this.scopes) {
if (Array.from(scope.values()).includes(generateVarName(this.varIndex))) {
isConfict = true;
break;
}
}
if (isConfict) {
this.varIndex++;
return this.getMangledName(originalName);
}
const mangled = generateVarName(this.varIndex++);
this.currentScope.set(originalName, mangled);
return mangled;
}
// 收集所有作用域的变量映射
getAllMappings() {
return this.scopes.concat([this.currentScope]).reduce((acc, scope) => {
scope.forEach((val, key) => acc.set(key, val));
return acc;
}, new Map());
}
}
// 混淆器核心
const obfuscator = {
// 处理字符串(支持转义字符和模板字符串)
async processStrings(code) {
const strRegex = /("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`)/g;
const matches = [];
let match;
// 先收集所有匹配结果(避免替换时影响正则匹配)
while ((match = strRegex.exec(code)) !== null) {
matches.push({
full: match[0],
content: match[0].slice(1, -1),
quote: match[0][0],
index: match.index
});
}
// 按索引倒序替换(避免索引偏移)
matches.sort((a, b) => b.index - a.index).forEach(async (item) => {
const encrypted = await cryptoUtil.encrypt(item.content);
// 生成解密代码(包含密钥和方法)
const decryptCode = `(async()=>{const p=${JSON.stringify(encrypted)};return await (${cryptoUtil.decrypt.toString()})(p)})()`;
code = code.slice(0, item.index) + decryptCode + code.slice(item.index + item.full.length);
});
return code;
},
// 混淆标识符(变量、函数、类)
mangleIdentifiers(code, config) {
const analyzer = new ScopeAnalyzer();
const funcRegex = /function\s+(\w+)\s*\(/g;
const classRegex = /class\s+(\w+)\s*/g;
const varRegex = /(var|let|const)\s+([^=;]+)|([(,]\s*)(\w+)(?=\s*[),])/g;
// 1. 处理函数名
if (config.mangleFuncs) {
code = code.replace(funcRegex, (match, funcName) => {
if (keywordSet.has(funcName)) return match;
analyzer.enterScope(); // 函数进入新作用域
const mangled = analyzer.getMangledName(funcName);
return `function ${mangled}(`;
});
}
// 2. 处理类名
if (config.mangleClasses) {
code = code.replace(classRegex, (match, className) => {
if (keywordSet.has(className)) return match;
analyzer.enterScope(); // 类进入新作用域
const mangled = analyzer.getMangledName(className);
return `class ${mangled} `;
});
}
// 3. 处理变量和参数
if (config.mangleVars) {
code = code.replace(varRegex, (match, declType, declVars, prefix, arrowParam) => {
if (declVars) {
// 处理变量声明(var/let/const)
return declVars.split(',').map(v => {
const varName = v.trim().split('=')[0].trim();
if (!varName || keywordSet.has(varName)) return v;
const mangled = analyzer.getMangledName(varName);
return v.replace(varName, mangled);
}).join(',');
} else if (arrowParam) {
// 处理箭头函数参数
const mangled = analyzer.getMangledName(arrowParam);
return `${prefix}${mangled}`;
}
return match;
});
}
// 4. 退出所有作用域(简化处理)
while (analyzer.scopes.length > 0) {
analyzer.exitScope();
}
// 5. 替换代码中所有标识符
const allMappings = analyzer.getAllMappings();
allMappings.forEach((mangled, original) => {
// 避免替换关键字和对象属性
const identifierRegex = new RegExp(`\\b${original}\\b(?!\\.)`, 'g');
code = code.replace(identifierRegex, mangled);
});
return code;
},
// 控制流扁平化
flattenControlFlow(code) {
if (!document.getElementById('flattenControl').checked) return code;
// 简单的if语句转为switch
return code.replace(/if\s*\((.*?)\)\s*\{/g, (match, condition) => {
return `switch(+(${condition})){case 1:{`;
}).replace(/}\s*else\s*\{/g, '}case 0:{');
},
// 主流程
async run(code) {
try {
updateProgress(10);
const config = {
mangleVars: document.getElementById('mangleVars').checked,
mangleFuncs: document.getElementById('mangleFuncs').checked,
mangleClasses: document.getElementById('mangleClasses').checked
};
// 1. 处理字符串
let processed = await this.processStrings(code);
updateProgress(40);
// 2. 混淆标识符
processed = this.mangleIdentifiers(processed, config);
updateProgress(70);
// 3. 控制流扁平化
processed = this.flattenControlFlow(processed);
updateProgress(100);
return processed;
} catch (e) {
throw new Error(`混淆失败: ${e.message}`);
}
}
};
// 解密器核心
const deobfuscator = {
async run(code) {
try {
updateProgress(30);
// 匹配加密 payload 并解密
const cryptoRegex = /\(async\(\)\=>\{const p=({.*?});return await \(\(.*?\)\)\(p\)\}\)\(\)/g;
const matches = [];
let match;
while ((match = cryptoRegex.exec(code)) !== null) {
matches.push({
full: match[0],
payload: JSON.parse(match[1]),
index: match.index
});
}
// 倒序替换解密结果
matches.sort((a, b) => b.index - a.index).forEach(async (item) => {
const decrypted = await cryptoUtil.decrypt(item.payload);
// 自动选择引号
const quote = decrypted.includes('"') ? "'" : '"';
code = code.slice(0, item.index) +
`${quote}${decrypted.replace(new RegExp(quote, 'g'), `\\${quote}`)}${quote}` +
code.slice(item.index + item.full.length);
});
updateProgress(100);
return code;
} catch (e) {
throw new Error(`解密失败: ${e.message}`);
}
}
};
// 对外接口
async function handleObfuscate() {
const input = document.getElementById('inputCode').value.trim();
if (!input) return showAlert('请输入要混淆的代码');
const obfBtn = document.getElementById('obfBtn');
obfBtn.disabled = true;
try {
const result = await obfuscator.run(input);
document.getElementById('outputCode').value = result;
showAlert('混淆成功', false);
} catch (e) {
showAlert(e.message);
} finally {
obfBtn.disabled = false;
}
}
async function handleDeobfuscate() {
const input = document.getElementById('inputCode').value.trim();
if (!input) return showAlert('请输入要解密的代码');
const deobfBtn = document.getElementById('deobfBtn');
deobfBtn.disabled = true;
try {
const result = await deobfuscator.run(input);
document.getElementById('outputCode').value = result;
showAlert('解密成功', false);
} catch (e) {
showAlert(e.message);
} finally {
deobfBtn.disabled = false;
}
}
function copyResult() {
const output = document.getElementById('outputCode');
if (!output.value) return showAlert('没有可复制的内容');
output.select();
document.execCommand('copy');
showAlert('复制成功', false);
}
</script>
</body>
</html>
最新发布