JS解密&反混淆
2021-03-13 23:23:1393
JS解密文档
1. js加密(高级)
加密网站
https://www.sojson.com/javascriptobfuscator.html
加密流程
首先将函数调用由点调用的形式转换为通过[“函数名”]的形式调用;
建立一个数组,保存js代码中的函数名和其他字符串,在使用函数名或字符串的时候用数组及下标替代。
解密代码
<html><head>
<meta charset="UTF-8"/>
<title>sojson js高级加密专破工具, https://www.sojson.com/javascriptobfuscator.html</title></head><body><form action="#">
<textarea name="js-code" id="js-code" cols="100" rows="30"></textarea>
<button id="decode-btn" type="button">DECODE</button></form><script type="text/javascript">
!function () {
document.getElementById("decode-btn").addEventListener("click", event => {
const jsCodeBox = document.getElementById("js-code");
const rawJs = jsCodeBox.value;
let decodeJs = dropSignature(rawJs);
decodeJs = replaceDictionaryIndexReference(decodeJs);
decodeJs = squareBracketsToDot(decodeJs);
jsCodeBox.value = decodeJs;
/**
* 字典引用替换为字面值常量
*
* @param rawJs
* @returns {*}
*/
function replaceDictionaryIndexReference(rawJs) {
const dictionaryNameSet = extractDictionaryNames(rawJs);
let decodeJs = rawJs;
dictionaryNameSet.forEach(dicName => {
// 将字典声明于当前上下文环境
const dicCode = new RegExp("(var\\s+|)" + dicName + "\\s*=\\s*\\[.+?\\];").exec(decodeJs)[0];
eval(dicCode);
console.log('dicCode:\n', dicCode);
// 将访问到此变量的地方引用替换为字面值
let isChange = false;
decodeJs = decodeJs.replace(new RegExp(dicName + "\\[(0x)?\\d+\\]", "g"), index => {
const dicIndex = parseInt(/\[((0x)?\d+)\]/g.exec(index)[1]);
let result = eval(dicName + "[" + dicIndex + "]");
// 对于文本,需要加上双引号
if (!result.match(/^\d+$/)) {
result = "\"" + result + "\"";
}
isChange = true;
return result;
});
// 如果此变量被使用过,则将其从原文中清除
if (isChange) {
decodeJs = decodeJs.replace(dicCode, "");
}
});
console.log('decodeJs:\n', decodeJs);
return decodeJs;
}
/**
* 抽取出所有字典名称
*
* @param rawJs
* @returns {Set}
*/
function extractDictionaryNames(rawJs) {
const re = /(_+\w+?)\s*=\s*\[.+?]/g;
// const re = /(_0x\w+?)\s*=/g;
const dictionaryNameSet = new Set();
while (dicName = re.exec(rawJs)) {
dictionaryNameSet.add(dicName[1]);
}
return dictionaryNameSet;
}
/**
* 方法调用尽量由["foo"]的形式转为点调用
*
* @param decodeJs
* @returns {string | void | *}
*/
function squareBracketsToDot(decodeJs) {
return decodeJs.replace(/\w+\[('|")\w+('|")]/g, call => {
const nameAndAttr = call.replace("[\"", " ").replace("\"]", "").split(" ");
console.log('nameAndAttr:\n',nameAndAttr);
try {
// 只替换name在当前上下文中已存在并且attr的类型是function
if (typeof eval(nameAndAttr[0] + "." + nameAndAttr[1]) === "function") {
return nameAndAttr[0] + "." + nameAndAttr[1];
}
} catch (e) {
console.log("cannot replace: " + call);
}
return call;
});
}
/**
* 丢弃作者的签名
*
* @param encodeJs
* @returns {string | void | *}
*/
function dropSignature(encodeJs) {
return encodeJs.replace(/^var __encode.+?\(window\);/, "");
}
});
}();</script></body></html>解密流程
首先去掉作者的签名;
提取出数组中所有的字符串,并在当前上下文环境建立相同的字符串数组;
将使用到数组的地方(数组名和下标)替换为对应的字符串;
将调用函数的方式由[“f”]转换为点调用。
示例
加密前:console.log("ok");
加密后
var __encode ='sojson.com', _0xb483=["\x5F\x64\x65\x63\x6F\x64\x65","\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x73\x6F\x6A\x73\x6F\x6E\x2E\x63\x6F\x6D\x2F\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x6F\x62\x66\x75\x73\x63\x61\x74\x6F\x72\x2E\x68\x74\x6D\x6C"];(function(_0xd642x1){_0xd642x1[_0xb483[0]]= _0xb483[1]})(window);var _0x7d24=["\x6F\x6B","\x6C\x6F\x67"];console[_0x7d24[0x1]](_0x7d24[0x0])加密:
建立一个数组为a=["\x6c\x6f\x67","\x6f\x6b"],将原来的代码替换为console[a[0]](a[1])。
解密:
在当前上下文建立数组[“log”,“ok”],并在通过数组下标访问数组的地方将其替换为数组中的字符串,得到console[“log”](“ok”);
然后转换成console.log(“ok”)的形式。
2. js代码混淆
加密网站
https://www.sojson.com/jscodeconfusion.html
混淆流程
首先将函数调用由点调用的形式转换为通过[“函数名”]的形式调用;
解密代码
<html><head>
<meta charset="UTF-8"/>
<title>sojson js代码混淆专破工具, https://www.sojson.com/jscodeconfusion.html</title></head><body><form action="#">
<textarea name="js-code" id="js-code" cols="100" rows="30"></textarea>
<button id="decode-btn" type="button">DECODE</button></form><script type="text/javascript">
!function() {
document.getElementById("decode-btn").addEventListener("click", event => {
const jsCodeBox = document.getElementById("js-code");
const rawJs = jsCodeBox.value;
let decodeJs = dispose(rawJs);
jsCodeBox.value = decodeJs;
/**
* 方法调用尽量由["foo"]的形式转为点调用
*
* @param rawJs
* @returns {string | void | *}
*/
function dispose(rawJs) {
let decodeJs = rawJs.replace(/(\\x|\\u)[0-9a-fA-F]+/g, ch => {
let rch = eval("\"" + ch + "\"");
return rch;
});
return decodeJs.replace(/\w+\[('|")\w+('|")]/g, call => {
const nameAndAttr = call.replace(/'/g,"\"").replace("[\"", " ").replace("\"]", "").split(" ");
console.log('nameAndAttr:\n',nameAndAttr);
try {
// 只替换name在当前上下文中已存在并且attr的类型是function
if (typeof eval(nameAndAttr[0] + "." + nameAndAttr[1]) === "function") {
return nameAndAttr[0] + "." + nameAndAttr[1];
}
} catch (e) {
console.log("cannot replace: " + call);
}
return call;
});
}
});
}();</script></body></html>解密流程
首先将字符串由十六进制\x表示的形式转换为字符形式;
将调用函数的方式由[“f”]转换为点调用。
示例
混淆前:console.log("ok");
混淆后:console['\x6c\x6f\x67']("\x6f\x6b");
混淆:
console[’\x6c\x6f\x67’]("\x6f\x6b");
解密:
首先转换为console[“log”](“ok”);
然后转换为console.log(“ok”)。
3. js混合加密
加密网站
https://www.sojson.com/charEncode.html
加密流程
将代码的每个字符都转换为10进制ascii码的形式,每两个ascii码之间用一个英文字符拼接在一起。
解密代码
<html><head>
<meta charset="UTF-8"/>
<title>sojson js混合加密专破工具, https://www.sojson.com/charEncode.html</title></head><body><form action="#">
<textarea name="js-code" id="js-code" cols="100" rows="30"></textarea>
<button id="decode-btn" type="button">DECODE</button></form><script type="text/javascript">
!function() {
document.getElementById("decode-btn").addEventListener("click", event => {
const jsCodeBox = document.getElementById("js-code");
const rawJs = jsCodeBox.value;
let decodeJs = dispose(rawJs);
jsCodeBox.value = decodeJs;
/**
* 解密JS
*
* @param rawJs
* @returns {string | void | *}
*/
function dispose(rawJs) {
let decodeJs = /\(null,.*\[a-zA-Z\]\{1,\}\/\)/g.exec(rawJs);
decodeJs = decodeJs[0].substring(6);
console.log(decodeJs);
let tmp = eval(decodeJs);
decodeJs = "";
for (let i=0; i<tmp.length; i++){
decodeJs += String.fromCharCode(tmp[i]);
}
return decodeJs;
}
});
}();</script></body></html>解密流程
将加密后的代码通过split()函数以英文字符切割,转换为一个数组,每个数组元素即是一个ascii码,将每个ascii码转换成字符拼接起来即可。
示例
加密前:console.log("ok");
加密后:
['sojson.v4']["\x66\x69\x6c\x74\x65\x72"]["\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72"](((['sojson.v4']+[])["\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72"]['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65']['\x61\x70\x70\x6c\x79'](null,"99L111s110J115m111r108o101O46A108E111u103R40H34x111Q107I34H41R59"['\x73\x70\x6c\x69\x74'](/[a-zA-Z]{1,}/))))('sojson.v4');加密:
加密后的核心部分是:“99o111h110T115s111c108z101N46X108B111W103v40F34t111t107H34V41”;
解密:
首先通过split()函数的到数组[“99”, “111”, …],将所有数组元素转换为字符并拼接成字符串即可。
4. eval加密解密
加密网站
https://www.w3xue.com/tools/jseval/
加密流程
提取代码中所有的单词(函数名、字符串等),并建立一个数组存放所有的单词;
将所有单词替换为其数组下标的62进制(大小写英文字母及数字,26+26+10=62)的形式;
有一个固定的函数,将加密后的代码、数组长度及数组的内容作为该函数的参数拼接成字符串即可。
解密代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>eval解密/加密在线工具</title><script> a=62; function encode() { //加密
var code = document.getElementById('code').value;
code = code.replace(/[\r\n]+/g, '');
code = code.replace(/'/g, "\\'");
var tmp = code.match(/\b(\w+)\b/g);
console.log('tmp:\n',tmp);
console.log('code:\n',code);
tmp.sort();
var dict = [];
var i, t = '';
for(var i=0; i<tmp.length; i++) {
if(tmp[i] != t) dict.push(t = tmp[i]);
}
var len = dict.length;
var ch;
for(i=0; i<len; i++) {
ch = num(i); //转换为62进制数
code = code.replace(new RegExp('\\b'+dict[i]+'\\b','g'), ch);
if(ch == dict[i]) dict[i] = '';
}
document.getElementById('code').value = "eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}("
+ "'"+code+"',"+a+","+len+",'"+ dict.join('|')+"'.split('|'),0,{}))"; } function num(c) {
console.log('numc:\n',c);
return(c<a?'':num(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36)); } function run() {
eval(document.getElementById('code').value); } function decode() { //解密
var code = document.getElementById('code').value;
code = code.replace(/^eval/, '');
document.getElementById('code').value = eval(code); } </script> <textarea id=code cols=80 rows=20> </textarea><br> <input type=button onclick=encode() value=加密> <input type=button onclick=decode() value=解密>解密流程
去掉最前面的eval四个字符,粘贴到命令行回车中,就会运行固定的函数,而那个函数的返回值就是解密后的代码。
示例
加密前:console.log("ok");
加密后:
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('0.1("2");',62,3,'console|log|ok'.split('|'),0,{}))加密后得到的密文如下:
eval(function(p, a, c, k, e, d) {
e = function(c) {
return (c < a ? "": e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
};
if (!''.replace(/^/, String)) {
while (c--) d[e(c)] = k[c] || e(c);
k = [function(e) {
return d[e]
}];
e = function() {
return '\\w+'
};
c = 1;
};
while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]);
return p;} ('0.1("2");', 62, 3, 'console|log|ok'.split('|'), 0, {}))这是一个固定的函数,关键部分是函数的参数;
'0.1(“2”);'即是加密后的代码;
3是数组长度;
'console|log|ok’即是加密时用到的数组的内容。