JavaScript混淆

前言

当今互联网时代,JavaScript已经成为了web前端开发的重点技术之一。其中,JavaScript代码的安全性问题一直是关注的焦点。为了保护JavaScript代码的安全性,很多人对其进行加密处理,众所周知,对于单纯的加密算法,通过反向工程或逆向分析也能够破解。在此情况下,JavaScript代码混淆技术成为了一种应对加密破解的有效措施。
 


提示:以下是本篇文章正文内容,下面案例可供参考

  • 一、js混淆是什么?

JavaScript混淆是一种将JavaScript代码转换成难以阅读和理解的形式的过程。这可以通过压缩、重命名变量和函数、删除注释和空格、使用特殊字符和添加无用代码等方式实现。混淆后的代码在计算机中仍然可以运行,但在人类眼中很难理解和修改。这可以有效地保护JavaScript代码免受未经授权的访问和篡改。
 

二、js混淆作用

JavaScript混淆是保护JavaScript代码和数据安全的重要技术。为了防止爬虫通过分析js内容,轻易的还原出网站的加密逻辑,而做出的安全防御手段之一,常常将一个逻辑简单的可能十几行算法代码思路,通过变量混淆替换、增加冗余无效代码、增加无限debugger反调试功能、增加多层的套娃式if~else代码、以及埋陷阱等混淆成几千行的js代码,给人第一印象难读,对入门分析者造成恐惧心理。

三、JavaScript 混淆技术主要有以下几种:

  • 变量混淆 将带有含意的变量名、方法名、常量名随机变为无意义的类乱码字符串,降低代码可读性,如转成单个字符或十六进制字符串。

  • 字符串混淆 将字符串阵列化集中放置、并可进行 MD5 或 Base64 加密存储,使代码中不出现明文字符串,这样可以避免使用全局搜索字符串的方式定位到入口点。

  • 属性加密 针对 JavaScript 对象的属性进行加密转化,隐藏代码之间的调用关系。

  • 控制流平坦化 打乱函数原有代码执行流程及函数调用关系,使代码逻变得混乱无序。

  • 僵尸代码 随机在代码中插入无用的僵尸代码、僵尸函数,进一步使代码混乱。

  • 调试保护 基于调试器特性,对当前运行环境进行检验,加入一些强制调试 debugger 语句,使其在调试模式下难以顺利执行 JavaScript 代码。

  • 多态变异 使 JavaScript 代码每次被调用时,将代码自身即立刻自动发生变异,变化为与之前完全不同的代码,即功能完全不变,只是代码形式变异,以此杜绝代码被动态分析调试。

  • 锁定域名 使 JavaScript 代码只能在指定域名下执行。

  • 反格式化 如果对 JavaScript 代码进行格式化,则无法执行,导致浏览器假死。

  • 特殊编码 将 JavaScript 完全编码为人不可读的代码,如表情符号、特殊表示内容等等

四、常见的混淆 

URL:JS加密,JS 安全加密, AAencode 加密 —在线加密 (sojson.com)

1)、OB混淆

OB 混淆全称 Obfuscator,Obfuscator 其实就是混淆的意思,官网:https://obfuscator.io/ ,其作者是一位叫 Timofey Kachalov 的俄罗斯JavaScript开发工程师,早在 2016 年就发布了第一个版本。

OB 混淆具有以下特征:

1、一般由一个大数组或者含有大数组的函数、一个自执行函数、解密函数和加密后的函数四部分组成;

2、函数名和变量名通常以 _0x 或者 0x 开头,后接 1~6 位数字或字母组合;

3、自执行函数,进行移位操作,有明显的 push、shift 关键字;

例如在上面的例子中,_0x3ed1() 方法就定义了一个大数组,自执行函数里有 push、shift 关键字,主要是对大数组进行移位操作,_0x2e12() 就是解密函数,hi() 就是加密后的函数

 

(function(_0x1cbb42, _0x27ca96) {
    var _0x35e2ac = _0x2e12
      , _0x22ba7c = _0x1cbb42();
    while (!![]) {
        try {
            var _0x108d6f = -parseInt(_0x35e2ac(0x1e6)) / 0x1 + parseInt(_0x35e2ac(0x1eb)) / 0x2 + parseInt(_0x35e2ac(0x1e5)) / 0x3 * (-parseInt(_0x35e2ac(0x1ef)) / 0x4) + parseInt(_0x35e2ac(0x1ed)) / 0x5 + parseInt(_0x35e2ac(0x1ee)) / 0x6 + parseInt(_0x35e2ac(0x1e9)) / 0x7 * (parseInt(_0x35e2ac(0x1e7)) / 0x8) + -parseInt(_0x35e2ac(0x1ea)) / 0x9 * (parseInt(_0x35e2ac(0x1ec)) / 0xa);
            if (_0x108d6f === _0x27ca96)
                break;
            else
                _0x22ba7c['push'](_0x22ba7c['shift']());
        } catch (_0x55c1fe) {
            _0x22ba7c['push'](_0x22ba7c['shift']());
        }
    }
}(_0x3ed1, 0x4cc24));
function _0x2e12(_0x57166b, _0x567174) {
    var _0x3ed14f = _0x3ed1();
    return _0x2e12 = function(_0x2e123b, _0xa39d85) {
        _0x2e123b = _0x2e123b - 0x1e5;
        var _0x532ba1 = _0x3ed14f[_0x2e123b];
        return _0x532ba1;
    }
    ,
    _0x2e12(_0x57166b, _0x567174);
}
function _0x3ed1() {
    var _0x5dda6c = ['1115632iBYQbo', '54eYZLaO', '767914jEsBIU', '744360nxCbIq', '13950Avaqin', '2375400wOmjhe', '60CEfoUI', '20127fpZoZw', '239744ExCnkT', '16trzBpq', 'log'];
    _0x3ed1 = function() {
        return _0x5dda6c;
    }
    ;
    return _0x3ed1();
}
function hi() {
    var _0xf7fbc1 = _0x2e12;
    console[_0xf7fbc1(0x1e8)]('Hello\x20World!');
}
hi();

2)、eval混淆

  • 定义: eval(string) ,可计算某个字符串,并执行其中的的 JavaScript 代码。 有返回值
  • 举例
// eval()函数使用举例
a=eval('(function xxx(){return "hello world"})()');
console.log(a)  // hello world

// 将上边的eval字体进行base64加密
xxx=btoa('(function xxx(){return "hello world"})()');
console.log(xxx)  // KGZ1bmN0aW9uIHh4eCgpe3JldHVybiAiaGVsbG8gd29ybGQifSkoKQ==

// 然后混淆的时候,可能就会放入加密的东西
console.log(eval(atob('KGZ1bmN0aW9uIHh4eCgpe3JldHVybiAiaGVsbG8gd29ybGQifSkoKQ==\n')));
  • 缺点 :想要运行字符串的值,只能通过明文的eval()函数执行,然后里面的字符串可能是多层加密的,我们通常在eval处下断点。

        想要运行字符串的值,只能通过明文的eval()函数执行,然后里面的字符串可能是多层加密的,我们通常在eval处下断点。

3)、AAEncode混淆(表情包加密)

  • js支持Unicode,因此支持Unicode里收录的所有国家语言,如果我们有的语言和我们定义的变量长得很相似(例如0和o),那么因此有了这种混淆。

示例:console.log("hello world!");

  •  解决方法:
  1. 控制台,增删代码,让其报错,即可在虚拟机(VM)处看见源码 
  2. 在线调试,在 AAEncode 代码第一行下断点,然后一步一步执行,最终也会在虚拟机(VM)里看到源码

 4)、jsfuck混淆

  • 思想: jsfuck源于一门编程语言brainfuck,其主要的思想就是只使用8种特定的符号来编写代码。而jsfuck也是沿用了这个思想,它仅仅使用6种符号来编写代码。它们分别是(、)、+、[、]、!
  • 示例:console.log("hello world!");

 

  • 解决方法:

  1. 如果是非虚拟机的数字/字符串加密使用解密工具:JS 不可逆加密,JS fuck 加密、JavaScript在线加密
  2. 上述解决不了只能一段一段手动控制台复制得结果
  3. 如果是虚拟机加密,让其强制报错

 5)、JJEncode混淆

  • 示例:alert("Hello, JavaScript" );

 

6)、sojson混淆

它可以为js代码添加加密混淆压缩成一行防止格式化死代码注入等属性。

  • 加密混淆:将代码中的所有标识符 (变量名, 函数名) 替换成没有意义的以下划线开始的十六进制数字,如 _0x546d, _0x28239d等。
  • 压缩成一行: 将原本多行的代码都写到一行。
  • 防止格式化:如果调试者希望借助 IDE 对代码进行格式化, 代码将无法正常运行, 陷入卡死状态。
  • 死代码注入:如果调试这对代码进行了格式化, 陷入的卡死状态就是注入的死代码导致的

 示例:

JS原文:

(function(w, d) { 
 w.update = "2023年07月16日15:24:29更新"; 
 d.info = "这个是一个本站对JavaScript 脚本的一个最牛加密,兼容性适中,解密难度极大"; 
})(window, document);

js混淆后:

var version_ = 'jsjiami.com.v7';
function _0x56f8(_0x239c5e, _0x4296e5) {
    var _0x342bf4 = _0x342b();
    return _0x56f8 = function(_0x56f859, _0x432900) {
        _0x56f859 = _0x56f859 - 0xeb;
        var _0x2e680a = _0x342bf4[_0x56f859];
        if (_0x56f8['OGfwbD'] === undefined) {
            var _0x2c3003 = function(_0x116e29) {
                var _0x4e35db = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';
                var _0x32ef41 = ''
                  , _0x3c2092 = '';
                for (var _0x4f8508 = 0x0, _0x28e3e8, _0x3c5243, _0x56e51d = 0x0; _0x3c5243 = _0x116e29['charAt'](_0x56e51d++); ~_0x3c5243 && (_0x28e3e8 = _0x4f8508 % 0x4 ? _0x28e3e8 * 0x40 + _0x3c5243 : _0x3c5243,
                _0x4f8508++ % 0x4) ? _0x32ef41 += String['fromCharCode'](0xff & _0x28e3e8 >> (-0x2 * _0x4f8508 & 0x6)) : 0x0) {
                    _0x3c5243 = _0x4e35db['indexOf'](_0x3c5243);
                }
                for (var _0x49fa53 = 0x0, _0x151cea = _0x32ef41['length']; _0x49fa53 < _0x151cea; _0x49fa53++) {
                    _0x3c2092 += '%' + ('00' + _0x32ef41['charCodeAt'](_0x49fa53)['toString'](0x10))['slice'](-0x2);
                }
                return decodeURIComponent(_0x3c2092);
            };
            var _0x1ca9f8 = function(_0x57c253, _0x3c21bc) {
                var _0x2a0b7d = [], _0x4cb5fb = 0x0, _0x4d43fe, _0x99a74b = '';
                _0x57c253 = _0x2c3003(_0x57c253);
                var _0x149167;
                for (_0x149167 = 0x0; _0x149167 < 0x100; _0x149167++) {
                    _0x2a0b7d[_0x149167] = _0x149167;
                }
                for (_0x149167 = 0x0; _0x149167 < 0x100; _0x149167++) {
                    _0x4cb5fb = (_0x4cb5fb + _0x2a0b7d[_0x149167] + _0x3c21bc['charCodeAt'](_0x149167 % _0x3c21bc['length'])) % 0x100,
                    _0x4d43fe = _0x2a0b7d[_0x149167],
                    _0x2a0b7d[_0x149167] = _0x2a0b7d[_0x4cb5fb],
                    _0x2a0b7d[_0x4cb5fb] = _0x4d43fe;
                }
                _0x149167 = 0x0,
                _0x4cb5fb = 0x0;
                for (var _0x244006 = 0x0; _0x244006 < _0x57c253['length']; _0x244006++) {
                    _0x149167 = (_0x149167 + 0x1) % 0x100,
                    _0x4cb5fb = (_0x4cb5fb + _0x2a0b7d[_0x149167]) % 0x100,
                    _0x4d43fe = _0x2a0b7d[_0x149167],
                    _0x2a0b7d[_0x149167] = _0x2a0b7d[_0x4cb5fb],
                    _0x2a0b7d[_0x4cb5fb] = _0x4d43fe,
                    _0x99a74b += String['fromCharCode'](_0x57c253['charCodeAt'](_0x244006) ^ _0x2a0b7d[(_0x2a0b7d[_0x149167] + _0x2a0b7d[_0x4cb5fb]) % 0x100]);
                }
                return _0x99a74b;
            };
            _0x56f8['Dtwoep'] = _0x1ca9f8,
            _0x239c5e = arguments,
            _0x56f8['OGfwbD'] = !![];
        }
        var _0x2898ad = _0x342bf4[0x0]
          , _0x5dd41f = _0x56f859 + _0x2898ad
          , _0x3ba330 = _0x239c5e[_0x5dd41f];
        return !_0x3ba330 ? (_0x56f8['lYcCWy'] === undefined && (_0x56f8['lYcCWy'] = !![]),
        _0x2e680a = _0x56f8['Dtwoep'](_0x2e680a, _0x432900),
        _0x239c5e[_0x5dd41f] = _0x2e680a) : _0x2e680a = _0x3ba330,
        _0x2e680a;
    }
    ,
    _0x56f8(_0x239c5e, _0x4296e5);
}
(function(_0x57cb6, _0x4314f7, _0x12aca8, _0x16b392, _0x1875bd, _0x432797, _0x394cbe) {
    return _0x57cb6 = _0x57cb6 >> 0x1,
    _0x432797 = 'hs',
    _0x394cbe = 'hs',
    function(_0x7b10f6, _0x1a79f7, _0xd8cd20, _0x3f28ea, _0x385c4a) {
        var _0x2eb8f7 = _0x56f8;
        _0x3f28ea = 'tfi',
        _0x432797 = _0x3f28ea + _0x432797,
        _0x385c4a = 'up',
        _0x394cbe += _0x385c4a,
        _0x432797 = _0xd8cd20(_0x432797),
        _0x394cbe = _0xd8cd20(_0x394cbe),
        _0xd8cd20 = 0x0;
        var _0x7b2355 = _0x7b10f6();
        while (!![] && --_0x16b392 + _0x1a79f7) {
            try {
                _0x3f28ea = parseInt(_0x2eb8f7(0xfa, 'Iw8r')) / 0x1 + -parseInt(_0x2eb8f7(0xf8, 'mhb8')) / 0x2 + parseInt(_0x2eb8f7(0xf0, 'lK2M')) / 0x3 * (-parseInt(_0x2eb8f7(0xf5, 'og&u')) / 0x4) + -parseInt(_0x2eb8f7(0xec, '$CGx')) / 0x5 + parseInt(_0x2eb8f7(0xed, 'T^LQ')) / 0x6 + -parseInt(_0x2eb8f7(0xf4, 'sW9l')) / 0x7 * (-parseInt(_0x2eb8f7(0xf3, 'g]XJ')) / 0x8) + -parseInt(_0x2eb8f7(0xf1, 'sW9l')) / 0x9 * (-parseInt(_0x2eb8f7(0xeb, 'QMK%')) / 0xa);
            } catch (_0x495302) {
                _0x3f28ea = _0xd8cd20;
            } finally {
                _0x385c4a = _0x7b2355[_0x432797]();
                if (_0x57cb6 <= _0x16b392)
                    _0xd8cd20 ? _0x1875bd ? _0x3f28ea = _0x385c4a : _0x1875bd = _0x385c4a : _0xd8cd20 = _0x385c4a;
                else {
                    if (_0xd8cd20 == _0x1875bd['replace'](/[eWyXFGnhNPAERSqkCKLrg=]/g, '')) {
                        if (_0x3f28ea === _0x1a79f7) {
                            _0x7b2355['un' + _0x432797](_0x385c4a);
                            break;
                        }
                        _0x7b2355[_0x394cbe](_0x385c4a);
                    }
                }
            }
        }
    }(_0x12aca8, _0x4314f7, function(_0x28f1b0, _0x2ae9e5, _0x343433, _0x14fb7b, _0x8bf826, _0x11755b, _0x537a44) {
        return _0x2ae9e5 = '\x73\x70\x6c\x69\x74',
        _0x28f1b0 = arguments[0x0],
        _0x28f1b0 = _0x28f1b0[_0x2ae9e5](''),
        _0x343433 = '\x72\x65\x76\x65\x72\x73\x65',
        _0x28f1b0 = _0x28f1b0[_0x343433]('\x76'),
        _0x14fb7b = '\x6a\x6f\x69\x6e',
        (0x134e3c,
        _0x28f1b0[_0x14fb7b](''));
    });
}(0x18e, 0x6c4ae, _0x342b, 0xc9),
_0x342b) && (version_ = _0x342b);
(function(_0x1c9e5b, _0x5351dd) {
    var _0x275977 = _0x56f8
      , _0xd0dfd9 = {
        'PQflt': '这个是一个本站对JavaScript\x20脚本的一个最牛加密,兼容性适中,解密难度极大'
    };
    _0x1c9e5b['update'] = _0x275977(0xf7, 'sW9l'),
    _0x5351dd[_0x275977(0xfc, '7k)1')] = _0xd0dfd9['PQflt'];
}(window, document));
function _0x342b() {
    var _0x407a63 = (function() {
        return [version_, 'EFyjWshNKjGFSiraCqmXeinP.cEoPAWmggk.RvL7==', 'pSonW4VdLxvJdSosW4md', 'WQaKmSosnSkIWPruFSoYW6u', 'ivvAWQCpWPNdUmo0W63cQq', 'jNxdLSkfnvNcPmon', 'WQqRnCoDmCkfWQHrsSohW7q'].concat((function() {
            return ['WO/dShRcRCkiz8oaW6VcU8os', 'ymkZWOKevGZcL8orW7ddO8klua', 'WQaJnSoz5BM2W7pdHEACSchcKUAvUfxcKXHdWQhcUqldV+AzUEAuGG', 'W6NcSNBdRConWOlcSgDjd8kby8kg', 'WQ5Ww8k8iSkHkmklDd4b', 'WRSeWQtdTCoaWPaGW4pcLCodySky', 'iIXhWRGRbt3dOW'].concat((function() {
                return ['ohVcPJu', 'W7alF3jzWO5wdSkAWRu', 'W6pdRLhdRr4eW550f8kPW6r2W68', 'W5PrWOSgW6mNv1NcNYdcUZm', 'WRSgWQxdVSkDW4K7W4lcV8ov', 'W5ldR3ahD8kCzmo6WPtcSSoPW6OQ'];
            }()));
        }()));
    }());
    _0x342b = function() {
        return _0x407a63;
    }
    ;
    return _0x342b();
}
;var version_ = 'jsjiami.com.v7';


总结

以上,我们就介绍了接口加密技术和 JavaScript 的压缩、混淆和加密技术,知己知彼方能百战不殆,了解了原理,我们才能更好地去实现 JavaScript 的逆向。

参考文献

  • https://www.ruanyifeng.com/blog/2017/09/asmjs_emscripten.html
  • https://juejin.im/post/5cfcb9d25188257e853fa71c#heading-23
  • https://www.jianshu.com/p/326594cbd4fa
  • https://github.com/javascript-obfuscator/javascript-obfuscator
  • https://obfuscator.io/
  • https://www.sojson.com/jjencode.html
  • http://dean.edwards.name/packer/
  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值