包子

木森技术分享

路漫漫其修远兮,吾将上下而求索。

您现在的位置是:网站首页 > jQuery

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’即是加密时用到的数组的内容。