一款JavaScript 混淆(Obfuscator)工具(Tool)的研究(三) 字符串數組

 

對應加密選項:

此選項主要的作用就是"刪除字符串文字並將其放置在特殊數組中。例如,在var m =“ Hello World”中的字符串“ Hello World”;將替換爲對將在運行時檢索其值的函數的調用,例如:var m = _0xb0c3('0x1');".

下邊我們來一一進行測試。

使用源碼:

// Paste your JavaScript code here
function hi() {
  console.log("Hello World!");
  document.write("Hello World!")
  console.log("test1111");
  document.write("<h1>xxxx</h1>")
  console.log("test33333");
  console.log("111test");
  console.log("22test11");
  document.write("Hello 22test11")
}
hi();

1.使用選項[String Array]

混淆後代碼爲:

var _0x21af = [
    'write',
    '<h1>xxxx</h1>',
    'test1111',
    'Hello\x20World!',
    '22test11',
    'test33333',
    '111test',
    'Hello\x2022test11',
    'log'
];
var _0x13c7 = function (_0x21af39, _0x13c7e6) {
    _0x21af39 = _0x21af39 - 0x0;
    var _0x1d5715 = _0x21af[_0x21af39];
    return _0x1d5715;
};
function hi() {
    console[_0x13c7('0x8')](_0x13c7('0x3'));
    document[_0x13c7('0x0')](_0x13c7('0x3'));
    console[_0x13c7('0x8')](_0x13c7('0x2'));
    document[_0x13c7('0x0')](_0x13c7('0x1'));
    console[_0x13c7('0x8')](_0x13c7('0x5'));
    console[_0x13c7('0x8')](_0x13c7('0x6'));
    console[_0x13c7('0x8')](_0x13c7('0x4'));
    document[_0x13c7('0x0')](_0x13c7('0x7'));
}
hi();

這個是最簡單的情況,就是把源碼中的字符串和一些函數名抽取出來,放在一個字符串數組(_0x21af )中,然後通過函數_0x13c7來獲取具體的字符串,調用函數時傳入的參數就是原字符串在數組中的下標。

此時_0x13c7('0x8')等價於_0x21af[8]

2.使用選項[String Array][Rotate String Array]

混淆後代碼爲:

var _0x3ae6 = [
    '111test',
    '22test11',
    'Hello\x2022test11',
    'log',
    'Hello\x20World!',
    'write',
    'test1111',
    '<h1>xxxx</h1>',
    'test33333'
];
(function (_0x4ee67b, _0x3ae658) {
    var _0x127781 = function (_0x42f296) {
        while (--_0x42f296) {
            _0x4ee67b['push'](_0x4ee67b['shift']());
        }
    };
    _0x127781(++_0x3ae658);
}(_0x3ae6, 0x78));
var _0x1277 = function (_0x4ee67b, _0x3ae658) {
    _0x4ee67b = _0x4ee67b - 0x0;
    var _0x127781 = _0x3ae6[_0x4ee67b];
    return _0x127781;
};
function hi() {
    console[_0x1277('0x0')](_0x1277('0x1'));
    document[_0x1277('0x2')](_0x1277('0x1'));
    console[_0x1277('0x0')](_0x1277('0x3'));
    document[_0x1277('0x2')](_0x1277('0x4'));
    console[_0x1277('0x0')](_0x1277('0x5'));
    console[_0x1277('0x0')](_0x1277('0x6'));
    console[_0x1277('0x0')](_0x1277('0x7'));
    document[_0x1277('0x2')](_0x1277('0x8'));
}
hi();

相比較上一個選項,[1]數組中字符串的順序變了,另外就是多了一個[2]自執行函數:

(function (_0x4ee67b, _0x3ae658) {
    var _0x127781 = function (_0x42f296) {
        while (--_0x42f296) {
            _0x4ee67b['push'](_0x4ee67b['shift']());
        }
    };
    _0x127781(++_0x3ae658);
}(_0x3ae6, 0x78));

此函數相當於:

fuction rotate_string_array(stringArray,nCount){
while(--nCount){
   //JavaScript Array對象  shift() 方法用於把數組的第一個元素從其中刪除,並返回第一個元素的值。
   //JavaScript Array對象  push() 方法可向數組的末尾添加一個或多個元素,並返回新的長度。
   stringArray.push(stringArray.shift())); 
}
}
rotate_string_array(_0x3ae6,0x78+1);

有了上邊的兩點改變([1][2]),此時_0x1277('0x0') 不等於 _0x3ae6[0]

兩者下標的轉化:

_0x1277('0x0') 相當於_0x3ae6[(('0x0')-0)+(0x78)%(_0x3ae6.length)))%(_0x3ae6.length))]:

'0x0' 即函數_0x1277的參數

(0x78) 即上邊自執行函數的參數

_0x3ae6.length 即數組的長度

帶入以上數據:

((('0x0')-0)+((0x78)%9))%9=3

即_0x1277('0x0') 等於 _0x3ae6[3]

有了這個下標換算關係,還原代碼就不難了。

3.使用選項[String Array][Rotate String Array][shuffle string array]

混淆後代碼爲:

var _0x634e = [
    'test33333',
    '22test11',
    'Hello\x2022test11',
    '111test',
    'Hello\x20World!',
    'write',
    '<h1>xxxx</h1>',
    'test1111',
    'log'
];
(function (_0x433e72, _0x634ef5) {
    var _0x152507 = function (_0x544bf0) {
        while (--_0x544bf0) {
            _0x433e72['push'](_0x433e72['shift']());
        }
    };
    _0x152507(++_0x634ef5);
}(_0x634e, 0x17e));
var _0x1525 = function (_0x433e72, _0x634ef5) {
    _0x433e72 = _0x433e72 - 0x0;
    var _0x152507 = _0x634e[_0x433e72];
    return _0x152507;
};
function hi() {
    console[_0x1525('0x4')](_0x1525('0x0'));
    document[_0x1525('0x1')](_0x1525('0x0'));
    console[_0x1525('0x4')](_0x1525('0x3'));
    document[_0x1525('0x1')](_0x1525('0x2'));
    console[_0x1525('0x4')](_0x1525('0x5'));
    console[_0x1525('0x4')](_0x1525('0x8'));
    console[_0x1525('0x4')](_0x1525('0x6'));
    document[_0x1525('0x1')](_0x1525('0x7'));
}
hi();

這個選項相比較之前只是打亂了字符串數組中字符串的位置,下標換算關係跟之前還是一樣。

4.使用選項[String Array][Rotate String Array][shuffle string array][string array encoding:base64]

混淆後代碼爲:

var _0x5093 = [
    'dGVzdDMzMzMz',
    'MTExdGVzdA==',
    'd3JpdGU=',
    'SGVsbG8gMjJ0ZXN0MTE=',
    'PGgxPnh4eHg8L2gxPg==',
    'MjJ0ZXN0MTE=',
    'SGVsbG8gV29ybGQh',
    'dGVzdDExMTE=',
    'bG9n'
];
(function (_0x557504, _0x509305) {
    var _0x195119 = function (_0x2a222e) {
        while (--_0x2a222e) {
            _0x557504['push'](_0x557504['shift']());
        }
    };
    _0x195119(++_0x509305);
}(_0x5093, 0x1cb));
var _0x1951 = function (_0x557504, _0x509305) {
    _0x557504 = _0x557504 - 0x0;
    var _0x195119 = _0x5093[_0x557504];
    if (_0x1951['EeSWAF'] === undefined) {
        (function () {
            var _0x207782;
            try {
                var _0xd7f008 = Function('return\x20(function()\x20' + '{}.constructor(\x22return\x20this\x22)(\x20)' + ');');
                _0x207782 = _0xd7f008();
            } catch (_0x50ca91) {
                _0x207782 = window;
            }
            var _0x36900f = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
            _0x207782['atob'] || (_0x207782['atob'] = function (_0x364904) {
                var _0x22a035 = String(_0x364904)['replace'](/=+$/, '');
                var _0x43ab01 = '';
                for (var _0x14a02b = 0x0, _0x39723d, _0x547739, _0x3ea29c = 0x0; _0x547739 = _0x22a035['charAt'](_0x3ea29c++); ~_0x547739 && (_0x39723d = _0x14a02b % 0x4 ? _0x39723d * 0x40 + _0x547739 : _0x547739, _0x14a02b++ % 0x4) ? _0x43ab01 += String['fromCharCode'](0xff & _0x39723d >> (-0x2 * _0x14a02b & 0x6)) : 0x0) {
                    _0x547739 = _0x36900f['indexOf'](_0x547739);
                }
                return _0x43ab01;
            });
        }());
        _0x1951['yMWxia'] = function (_0x376d0d) {
            var _0x62a356 = atob(_0x376d0d);
            var _0x49d7e5 = [];
            for (var _0x22b71c = 0x0, _0x15c3c = _0x62a356['length']; _0x22b71c < _0x15c3c; _0x22b71c++) {
                _0x49d7e5 += '%' + ('00' + _0x62a356['charCodeAt'](_0x22b71c)['toString'](0x10))['slice'](-0x2);
            }
            return decodeURIComponent(_0x49d7e5);
        };
        _0x1951['YuGjOc'] = {};
        _0x1951['EeSWAF'] = !![];
    }
    var _0x2a222e = _0x1951['YuGjOc'][_0x557504];
    if (_0x2a222e === undefined) {
        _0x195119 = _0x1951['yMWxia'](_0x195119);
        _0x1951['YuGjOc'][_0x557504] = _0x195119;
    } else {
        _0x195119 = _0x2a222e;
    }
    return _0x195119;
};
function hi() {
    console[_0x1951('0x8')](_0x1951('0x6'));
    document[_0x1951('0x2')](_0x1951('0x6'));
    console[_0x1951('0x8')](_0x1951('0x7'));
    document[_0x1951('0x2')](_0x1951('0x4'));
    console[_0x1951('0x8')](_0x1951('0x0'));
    console[_0x1951('0x8')](_0x1951('0x1'));
    console[_0x1951('0x8')](_0x1951('0x5'));
    document[_0x1951('0x2')](_0x1951('0x3'));
}
hi();

與前一個選項相比:

1.字符串數組中的所有字符串全部變成base64加密後的字符串。

2.多了一個base64的類 _0x1951:

_0x1951['EeSWAF'] =true  是一個bool變量,主要控制類的初始化。

_0x1951['YuGjOc'] = {}; 是一個字典,裏邊的鍵是序號,值是解密後的數據

_0x1951['yMWxia']  相當於base64.decode函數

 

此時的換算關係:

_0x1951(index)=base64.decode(_0x5093[(index+0x1cb%_0x5093.length)%_0x5093.length])

5.使用選項[String Array][Rotate String Array][shuffle string array][string array encoding:RC4]

混淆後代碼爲:

var _0x4ad7 = [
    'wpvCq8OSwqXCiGM3Zg==',
    'YcKFwpjDmk0Mwq7DhwfDlMOww4XCthE=',
    'w6XDvcO5',
    'wrvDkyg=',
    'wrc9w6TDjhg=',
    'MX84wozCmcKpw49zUMKMwrbDtQ==',
    'ZMOWwqQ=',
    'EMKdC3JZH8OXNnLDkTbDjV0=',
    'w6fCjRXCicKTw5vDtMKywrY=',
    'w5AAwoFKLsOxJQ==',
    'wptvwpJtJw==',
    'XsKSwp3Dgkc=',
    'QMKaXQ==',
    'F8K8ZcKnw6Bgw4QZwrcqwrTCow==',
    'HMKVw7jCssOmw5/Co8KW',
    'QMOWBA=='
];
(function (_0x3f83d1, _0x4ad746) {
    var _0x394a0f = function (_0x2c6096) {
        while (--_0x2c6096) {
            _0x3f83d1['push'](_0x3f83d1['shift']());
        }
    };
    _0x394a0f(++_0x4ad746);
}(_0x4ad7, 0x16f));
var _0x394a = function (_0x3f83d1, _0x4ad746) {
    _0x3f83d1 = _0x3f83d1 - 0x0;
    var _0x394a0f = _0x4ad7[_0x3f83d1];
    if (_0x394a['FkjqSs'] === undefined) {
        (function () {
            var _0x76ef1c = function () {
                var _0xae539f;
                try {
                    _0xae539f = Function('return\x20(function()\x20' + '{}.constructor(\x22return\x20this\x22)(\x20)' + ');')();
                } catch (_0x4d3156) {
                    _0xae539f = window;
                }
                return _0xae539f;
            };
            var _0x424f4c = _0x76ef1c();
            var _0x5ccca1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
            _0x424f4c['atob'] || (_0x424f4c['atob'] = function (_0x5f1fa6) {
                var _0x3bf491 = String(_0x5f1fa6)['replace'](/=+$/, '');
                var _0x350fd8 = '';
                for (var _0xc02741 = 0x0, _0x253fdf, _0x322cae, _0xc1ae51 = 0x0; _0x322cae = _0x3bf491['charAt'](_0xc1ae51++); ~_0x322cae && (_0x253fdf = _0xc02741 % 0x4 ? _0x253fdf * 0x40 + _0x322cae : _0x322cae, _0xc02741++ % 0x4) ? _0x350fd8 += String['fromCharCode'](0xff & _0x253fdf >> (-0x2 * _0xc02741 & 0x6)) : 0x0) {
                    _0x322cae = _0x5ccca1['indexOf'](_0x322cae);
                }
                return _0x350fd8;
            });
        }());
        var _0x56f8f9 = function (_0x493d85, _0x33f860) {
            var _0x37e233 = [], _0x2e2f97 = 0x0, _0x5ce7e9, _0x29536e = '', _0xb9cb80 = '';
            _0x493d85 = atob(_0x493d85);
            for (var _0x26a45f = 0x0, _0x483193 = _0x493d85['length']; _0x26a45f < _0x483193; _0x26a45f++) {
                _0xb9cb80 += '%' + ('00' + _0x493d85['charCodeAt'](_0x26a45f)['toString'](0x10))['slice'](-0x2);
            }
            _0x493d85 = decodeURIComponent(_0xb9cb80);
            var _0x4f18e0;
            for (_0x4f18e0 = 0x0; _0x4f18e0 < 0x100; _0x4f18e0++) {
                _0x37e233[_0x4f18e0] = _0x4f18e0;
            }
            for (_0x4f18e0 = 0x0; _0x4f18e0 < 0x100; _0x4f18e0++) {
                _0x2e2f97 = (_0x2e2f97 + _0x37e233[_0x4f18e0] + _0x33f860['charCodeAt'](_0x4f18e0 % _0x33f860['length'])) % 0x100;
                _0x5ce7e9 = _0x37e233[_0x4f18e0];
                _0x37e233[_0x4f18e0] = _0x37e233[_0x2e2f97];
                _0x37e233[_0x2e2f97] = _0x5ce7e9;
            }
            _0x4f18e0 = 0x0;
            _0x2e2f97 = 0x0;
            for (var _0x3ffe3c = 0x0; _0x3ffe3c < _0x493d85['length']; _0x3ffe3c++) {
                _0x4f18e0 = (_0x4f18e0 + 0x1) % 0x100;
                _0x2e2f97 = (_0x2e2f97 + _0x37e233[_0x4f18e0]) % 0x100;
                _0x5ce7e9 = _0x37e233[_0x4f18e0];
                _0x37e233[_0x4f18e0] = _0x37e233[_0x2e2f97];
                _0x37e233[_0x2e2f97] = _0x5ce7e9;
                _0x29536e += String['fromCharCode'](_0x493d85['charCodeAt'](_0x3ffe3c) ^ _0x37e233[(_0x37e233[_0x4f18e0] + _0x37e233[_0x2e2f97]) % 0x100]);
            }
            return _0x29536e;
        };
        _0x394a['ZISiij'] = _0x56f8f9;
        _0x394a['uXgOrO'] = {};
        _0x394a['FkjqSs'] = !![];
    }
    var _0x2c6096 = _0x394a['uXgOrO'][_0x3f83d1];
    if (_0x2c6096 === undefined) {
        if (_0x394a['MJufIp'] === undefined) {
            _0x394a['MJufIp'] = !![];
        }
        _0x394a0f = _0x394a['ZISiij'](_0x394a0f, _0x4ad746);
        _0x394a['uXgOrO'][_0x3f83d1] = _0x394a0f;
    } else {
        _0x394a0f = _0x2c6096;
    }
    return _0x394a0f;
};
function hi() {
    console[_0x394a('0x0', 'aimw')](_0x394a('0x6', '*DW8'));
    document[_0x394a('0x5', 'L*Y]')](_0x394a('0xe', 'Z9#C'));
    console[_0x394a('0xd', ']GOz')](_0x394a('0x1', 'KQ#T'));
    document[_0x394a('0xb', 'a%Hx')](_0x394a('0x8', ']GOz'));
    console[_0x394a('0x3', '@abs')](_0x394a('0x9', '!dX$'));
    console[_0x394a('0x7', ')NGv')](_0x394a('0xa', 'z4uj'));
    console[_0x394a('0x4', '4Ece')](_0x394a('0xf', '@he@'));
    document[_0x394a('0xc', '2RVh')](_0x394a('0x2', '2RVh'));
}
hi();

與前一個選項相比:

1.字符串數組中的所有字符串全部變成先rc4加密然後base64加密的字符串。

2.多了一個base64_Rc4的類 _0x394a :

_0x394a ['FkjqSs'] =true  是一個bool變量,主要控制類的初始化。

_0x394a ['uXgOrO'] = {}; 是一個字典,裏邊的鍵是序號,值是解密後的數據

_0x394a ['ZISiij']  相當於rc4.decode(base64.decode(index),key)函數

此時的換算關係:

0x16f:fun(_0x4ad7, 0x16f));  某匿名函數的值

_0x394a(index='0x0', key'aimw')=rc4.decode(base64.decode(_0x4ad7[(index+0x16f%_0x4ad7.length)%_0x4ad7.length]),key)

多了個rc4,其他無變化。

另外,根據文檔介紹,RC4選項比​​Base64選項慢大約30-35%,因此使用這個加密的應該不多。特別是庫文件。

4.使用選項[String Array][Rotate String Array][shuffle string array][string array encoding:base64][Split Strings Chunk Length:3]

混淆後代碼爲:

var _0x10f8 = [
    'MjJ0',
    'SGVs',
    'bGQh',
    'V29y',
    'eHg8',
    'dGVz',
    'd3Jp',
    'L2gx',
    'ZXN0',
    'MzMz',
    'dDEx',
    'PGgx',
    'bG8g',
    'Pnh4',
    'MTEx',
    'dDMz',
    'bG9n'
];
(function (_0x9f2e7f, _0x10f8ac) {
    var _0x83553e = function (_0x499d45) {
        while (--_0x499d45) {
            _0x9f2e7f['push'](_0x9f2e7f['shift']());
        }
    };
    _0x83553e(++_0x10f8ac);
}(_0x10f8, 0x8d));
var _0x8355 = function (_0x9f2e7f, _0x10f8ac) {
    _0x9f2e7f = _0x9f2e7f - 0x0;
    var _0x83553e = _0x10f8[_0x9f2e7f];
    if (_0x8355['mAxMIN'] === undefined) {
        (function () {
            var _0x7e1e19;
            try {
                var _0x5864ff = Function('return\x20(function()\x20' + '{}.constructor(\x22return\x20this\x22)(\x20)' + ');');
                _0x7e1e19 = _0x5864ff();
            } catch (_0x56e058) {
                _0x7e1e19 = window;
            }
            var _0x5bbe45 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
            _0x7e1e19['atob'] || (_0x7e1e19['atob'] = function (_0x4cb980) {
                var _0x4eb69e = String(_0x4cb980)['replace'](/=+$/, '');
                var _0x351318 = '';
                for (var _0x2e02e7 = 0x0, _0x14f3ca, _0x501ae4, _0x2ad81c = 0x0; _0x501ae4 = _0x4eb69e['charAt'](_0x2ad81c++); ~_0x501ae4 && (_0x14f3ca = _0x2e02e7 % 0x4 ? _0x14f3ca * 0x40 + _0x501ae4 : _0x501ae4, _0x2e02e7++ % 0x4) ? _0x351318 += String['fromCharCode'](0xff & _0x14f3ca >> (-0x2 * _0x2e02e7 & 0x6)) : 0x0) {
                    _0x501ae4 = _0x5bbe45['indexOf'](_0x501ae4);
                }
                return _0x351318;
            });
        }());
        _0x8355['TivOUi'] = function (_0xb1759e) {
            var _0x4a1351 = atob(_0xb1759e);
            var _0x27b605 = [];
            for (var _0x4053ee = 0x0, _0x148843 = _0x4a1351['length']; _0x4053ee < _0x148843; _0x4053ee++) {
                _0x27b605 += '%' + ('00' + _0x4a1351['charCodeAt'](_0x4053ee)['toString'](0x10))['slice'](-0x2);
            }
            return decodeURIComponent(_0x27b605);
        };
        _0x8355['BxgXBm'] = {};
        _0x8355['mAxMIN'] = !![];
    }
    var _0x499d45 = _0x8355['BxgXBm'][_0x9f2e7f];
    if (_0x499d45 === undefined) {
        _0x83553e = _0x8355['TivOUi'](_0x83553e);
        _0x8355['BxgXBm'][_0x9f2e7f] = _0x83553e;
    } else {
        _0x83553e = _0x499d45;
    }
    return _0x83553e;
};
function hi() {
    console[_0x8355('0xb')](_0x8355('0xd') + _0x8355('0x7') + _0x8355('0xf') + _0x8355('0xe'));
    document[_0x8355('0x1') + 'te'](_0x8355('0xd') + _0x8355('0x7') + _0x8355('0xf') + _0x8355('0xe'));
    console[_0x8355('0xb')](_0x8355('0x0') + _0x8355('0x5') + '11');
    document[_0x8355('0x1') + 'te'](_0x8355('0x6') + _0x8355('0x8') + _0x8355('0x10') + _0x8355('0x2') + '>');
    console[_0x8355('0xb')](_0x8355('0x0') + _0x8355('0xa') + _0x8355('0x4'));
    console[_0x8355('0xb')](_0x8355('0x9') + _0x8355('0x0') + 't');
    console[_0x8355('0xb')](_0x8355('0xc') + _0x8355('0x3') + '11');
    document[_0x8355('0x1') + 'te'](_0x8355('0xd') + _0x8355('0x7') + _0x8355('0xc') + _0x8355('0x3') + '11');
}
hi();

這個就是先把字符串按照設置的值分割,然後再加密放入數組中。

比如write 3個字符分割,則分割爲兩部分 wri 和te

wri加密後放入數組。

還原方法還是一樣。

 

至於剩下的選項:

1.Transform Object Keys  轉換對象鍵

Transforms (obfuscates) object keys.轉換(混淆)對象鍵。

For instance, this code var a = {enabled: true}; when obfuscated with this option will hide the enabled object key: var a = {}; a[_0x2ae0[('0x0')] = true;.例如,這段代碼var a = {enabled:true}; 當對此選項進行混淆時,將隱藏啓用的對象鍵:var a = {}; a [_0x2ae0 [('0x0')] = true;。

See the official documentation of the JavaScript Obfuscator on GitHub for a full example.有關完整示例,請參見GitHub上的JavaScript Obfuscator的官方文檔。

 

 

2.Escape Unicode Sequence

轉義Unicode序列

Converts all the strings to their unicode representation. For instance, the string "Hello World!" will be converted to "'\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21".將所有字符串轉換爲它們的unicode表示形式。 例如,字符串“ Hello World!”。 將被轉換爲“'\ x48 \ x65 \ x6c \ x6c \ x6f \ x20 \ x57 \ x6f \ x72 \ x6c \ x64 \ x21”。

This convertion is pretty easy to revert, and will increase the obfuscated code size greatly. It's not recommended on larger code bases.此轉換非常容易還原,並且將大大增加混淆的代碼大小。 不建議在較大的代碼庫中使用。

只是起到輔助作用,不再一一測試。

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章