【前端混淆】JavaScript Obfuscator

引言:前端

前端代碼是直接暴漏在瀏覽器中的,不少web攻擊都是經過直接debug業務邏輯找到漏洞進行攻擊,另外還有些喜歡「坐享其成」的分子暴力盜取他人網頁簡單修改後用來獲利,整體上來講就是前端的邏輯太容易讀懂了,本文主要基於JavaScript Obfuscator介紹一下前端混淆的基本思路。git

1、JavaScript Obfuscator簡介:github

  JavaScript Obfuscator是Timofey Kachalov開發的一款JS混淆工具,傳統的如uflifyJS等混淆工具主要都是用來壓縮代碼、下降資源加載時間的,混淆只是附帶屬性。JavaScript Obfuscator的主要目的就是爲了安全,保護前端代碼。web

 

2、JavaScript Obfuscator特性:編程

  JavaScript Obfuscator的混淆原理我就不介紹了,就是用工具對JS進行一下AST(抽象語法樹)分析、修改,再從新根據AST生成JS就能夠了,uglifyJS也能夠實現,給你們推薦一下 esprima http://esprima.org/,github上有一系列工具,用來作混淆和反混淆都很是好用。數組

  下面我直接講一下JavaScript Obfuscator的特性,主要特性包括:瀏覽器

  • 關鍵字提取,增長讀取難度:

  JavaScript Obfuscator會將JS裏面的關鍵字,如字符常量等提取出來放到數組中,調用的時候用數組下標的方式調用,這樣的話直接讀懂基本不可能了,要麼反AST處理下,要麼一步一步調試,工做量大增。安全

var test = "hello";
//處理後
var _0x7deb=['hello'];(function(_0xdf8359,_0x2abb06){var _0x4b8e4a=function(_0x3c281c){while(--_0x3c281c){_0xdf8359['push'](_0xdf8359['shift']());}};_0x4b8e4a(++_0x2abb06);}(_0x7deb,0x94));var _0xb7de=function(_0x4c7513,_0x1cb87c){_0x4c7513=_0x4c7513-0x0;var _0x96ade5=_0x7deb[_0x4c7513];return _0x96ade5;};var test=_0xb7de('0x0');

  ps:JavaScript Obfuscator這裏作的其實還不夠,還能夠進一步優化一下,業務相關不在這裏說了。app

  

  • 關鍵字編碼,進一步增長閱讀難度:

  從上面的混淆能夠看出,雖然作了關鍵字提取,但數組中 「hello」 仍是清晰可見,爲了進一步增長讀代碼難度,JavaScript Obfuscator利用了JS中16進制編碼會直接解碼的特性將關鍵字的Unicode進行了16進制編碼。函數式編程

var test = "hello";
//處理後
var _0x5f41=['\x68\x65\x6c\x6c\x6f'];(function(_0x265fed,_0x59b917){var _0x468703=function(_0x2e4674){while(--_0x2e4674){_0x265fed['push'](_0x265fed['shift']());}};_0x468703(++_0x59b917);}(_0x5f41,0xdd));var _0x15f4=function(_0x551d6e,_0x2697e4){_0x551d6e=_0x551d6e-0x0;var _0x40c0ad=_0x5f41[_0x551d6e];return _0x40c0ad;};var test=_0x15f4('0x0');

 

  • 關鍵字加密,增長手動調試難度:

   作了關鍵字提取後,假如一我的想要破解那麼必需要單步調試才能夠(先忽略反AST的狀況),JavaScript Obfuscator在這裏提供了兩種關鍵字加密方式用來對抗單步調試,base64加密和rc4加密,這樣處理後單步調試就會加大一些成本。

  

var test = "hello";
//關鍵字rc4加密
var _0x13b4=['\x77\x70\x4d\x72\x77\x36\x6a\x44\x67\x54\x4d\x3d'];(function(_0x5f376f,_0x4ee5e1){var _0x45c6a7=function(_0x40c574){while(--_0x40c574){_0x5f376f['push'](_0x5f376f['shift']());}};_0x45c6a7(++_0x4ee5e1);}(_0x13b4,0x174));var _0x413b=function(_0x3d9922,_0x37e804){_0x3d9922=_0x3d9922-0x0;var _0xbfa147=_0x13b4[_0x3d9922];if(_0x413b['initialized']===undefined){(function(){var _0x3e4f10=function(){var _0x1699ce;try{_0x1699ce=Function('return\x20(function()\x20'+'{}.constructor(\x22return\x20this\x22)(\x20)'+');')();}catch(_0x2d7a15){_0x1699ce=window;}return _0x1699ce;};var _0x3e7b6b=_0x3e4f10();var _0x2e450c='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x3e7b6b['atob']||(_0x3e7b6b['atob']=function(_0x4fedce){var _0x185f31=String(_0x4fedce)['replace'](/=+$/,'');for(var _0x3c6eda=0x0,_0x48064a,_0x5a5e47,_0x1c810e=0x0,_0x3443c2='';_0x5a5e47=_0x185f31['charAt'](_0x1c810e++);~_0x5a5e47&&(_0x48064a=_0x3c6eda%0x4?_0x48064a*0x40+_0x5a5e47:_0x5a5e47,_0x3c6eda++%0x4)?_0x3443c2+=String['fromCharCode'](0xff&_0x48064a>>(-0x2*_0x3c6eda&0x6)):0x0){_0x5a5e47=_0x2e450c['indexOf'](_0x5a5e47);}return _0x3443c2;});}());var _0x834c2=function(_0x56e849,_0x2be38f){var _0x3aca38=[],_0x1c774d=0x0,_0x49ad4c,_0x595dd4='',_0x5e8aba='';_0x56e849=atob(_0x56e849);for(var _0x295cae=0x0,_0xfbcfa1=_0x56e849['length'];_0x295cae<_0xfbcfa1;_0x295cae++){_0x5e8aba+='%'+('00'+_0x56e849['charCodeAt'](_0x295cae)['toString'](0x10))['slice'](-0x2);}_0x56e849=decodeURIComponent(_0x5e8aba);for(var _0x51a9e3=0x0;_0x51a9e3<0x100;_0x51a9e3++){_0x3aca38[_0x51a9e3]=_0x51a9e3;}for(_0x51a9e3=0x0;_0x51a9e3<0x100;_0x51a9e3++){_0x1c774d=(_0x1c774d+_0x3aca38[_0x51a9e3]+_0x2be38f['charCodeAt'](_0x51a9e3%_0x2be38f['length']))%0x100;_0x49ad4c=_0x3aca38[_0x51a9e3];_0x3aca38[_0x51a9e3]=_0x3aca38[_0x1c774d];_0x3aca38[_0x1c774d]=_0x49ad4c;}_0x51a9e3=0x0;_0x1c774d=0x0;for(var _0x4b8de1=0x0;_0x4b8de1<_0x56e849['length'];_0x4b8de1++){_0x51a9e3=(_0x51a9e3+0x1)%0x100;_0x1c774d=(_0x1c774d+_0x3aca38[_0x51a9e3])%0x100;_0x49ad4c=_0x3aca38[_0x51a9e3];_0x3aca38[_0x51a9e3]=_0x3aca38[_0x1c774d];_0x3aca38[_0x1c774d]=_0x49ad4c;_0x595dd4+=String['fromCharCode'](_0x56e849['charCodeAt'](_0x4b8de1)^_0x3aca38[(_0x3aca38[_0x51a9e3]+_0x3aca38[_0x1c774d])%0x100]);}return _0x595dd4;};_0x413b['rc4']=_0x834c2;_0x413b['data']={};_0x413b['initialized']=!![];}var _0x1cc8d3=_0x413b['data'][_0x3d9922];if(_0x1cc8d3===undefined){if(_0x413b['once']===undefined){_0x413b['once']=!![];}_0xbfa147=_0x413b['rc4'](_0xbfa147,_0x37e804);_0x413b['data'][_0x3d9922]=_0xbfa147;}else{_0xbfa147=_0x1cc8d3;}return _0xbfa147;};var test=_0x413b('0x0','\x29\x38\x24\x34');

 

  • 控制流變換,增長手動調試難度:

  從上面的JS看其實手動調試的難度還不夠高,JavaScript Obfuscator提供了一個控制流平展的能力,能夠用控制流來控制邏輯,增長調試的複雜度, 這樣處理後會發現當代碼量很大的時候手動debug困難就很是大了。

function testFn(){
    var test = "hello";
    if(test){
        test = "hello Devinn";
    }
  return test;
}

//處理後 爲了你們能看清將上面的方法都去掉了 這裏只處理控制流 而且作了格式化

function testFn() {
    var _0x25ac20 = {
        'roscj' : 'hello',
        'BjrCW' : 'hello\x20Devinn'
    };
    var _0x52a030 = _0x25ac20['roscj'];
    if (_0x52a030) {
        _0x52a030 = _0x25ac20['BjrCW'];
    }
    return _0x52a030;
}

 

  • 廢代碼注入,增長手動調試難度:

  若是增長了以上變換以及控制流難度還不夠的話,JavaScript Obfuscator還提供了廢代碼注入的機制,能夠隨機注入廢代碼,增長手動調試難度。

  

  • debug防禦,禁止手動調試:

  上面的思路都是在增長手動調試的難度,debug防禦可讓開啓控制檯的用戶一直卡在debugger控制檯上,這裏的實現思路比較暴力,一直在調用debugger,實際上能夠作些時間上的控制邏輯,你們能夠自由發揮。

 

var test = "hello";
//處理後 已格式化
(function () {
    var _0x4ca286 = new RegExp('function\x20*\x5c(\x20*\x5c)');
    var _0x4c73ba = new RegExp('\x5c+\x5c+\x20*_0x([a-f0-9]){4,6}');
    var _0x215cc4 = _0x203654('init');
    if (!_0x4ca286['test'](_0x215cc4 + 'chain') || !_0x4c73ba['test'](_0x215cc4 + 'input')) {
        _0x215cc4('0');
    } else {
        _0x203654();
    }
}
    ());
var test = 'hello';
function _0x203654(_0x53ac71) {
    function _0x13f874(_0x10526b) {
        if (typeof _0x10526b === 'string') {
            return function (_0x1146de) {}

            ['constructor']('while\x20(true)\x20{}')['apply']('counter');
        } else {
            if (('' + _0x10526b / _0x10526b)['length'] !== 0x1 || _0x10526b % 0x14 === 0x0) {
                (function () {
                    return !![];
                }
                    ['constructor']('debu' + 'gger')['call']('action'));
            } else {
                (function () {
                    return ![];
                }
                    ['constructor']('debu' + 'gger')['apply']('stateObject'));
            }
        }
        _0x13f874(++_0x10526b);
    }
    try {
        if (_0x53ac71) {
            return _0x13f874;
        } else {
            _0x13f874(0x0);
        }
    } catch (_0x2c3b47) {}

}
  • selfDefending禁止美化代碼:

   惡意在試調試代碼的時候都會使用devTools的美化功能,將代碼美化後進行調試,JavaScript Obfuscator針對這種狀況提供了selfDefending的功能,若是美化代碼整個JS會報錯沒法執行,原理就是一個CRC校驗,不詳細說了。

 

  • 域名鎖定,防止拖JS到本地修改調試:

  上面的debug防禦、代碼美化都是在JS裏面加了控制代碼實現的,若是將JS拖到本地去掉後就能夠繼續破解,JavaScript Obfuscator還作了一個域名鎖定的功能,即判斷當前域名是不是設置域名,不是就沒法執行下去。

  以上就是JavaScript Obfuscator的關鍵特性,雖然作了上面的各類處理,實際上單個靜態JS仍是能夠破解的,好比 「防止拖JS到本地修改調試」 ,實際上把相關代碼去除仍是能夠本地修改調試的,甚至高級一點的能夠用反AST的方式來破解調試。私覺得看待這個問題要立體的看,整個複雜度上來再想調試成本就很是高了,另外若是對抗反AST破解的狀況能夠將JS調整成動態,最安全的加密就是一次一密,JS作成一樣的就能夠了。

 

3、開發建議:

  全部的混淆器都要知足功能可用,全部全局變量,以及被全局變量引用的變量都不會被混淆器混淆,如對象屬性(其實也能夠處理,容易出錯),開發的時候能夠在關鍵的代碼上使用一些函數式編程,混淆會更完全一點。另外,若是兼容性容許的話能夠嘗試下asm.js,另外一個思路。

 

4、總結:

  本文主要介紹了一下JavaScript Obfuscator的關鍵特性,實際上只是想以這個工具爲例說一下前端代碼保護的一些思路,思路不限於JS。另外還有一些工具,如:jsFuck等,相關處理的思路均可以借鑑,你們自由發揮,有想法的話歡迎交流。

相關文章
相關標籤/搜索