├── .gitignore ├── LICENSE ├── README.md ├── decrypt-str.js ├── dist └── .gitkeep ├── hello-world-obf.js ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/*.js 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | package-lock.json 8 | 9 | # Editor directories and files 10 | .history 11 | .idea 12 | .vscode 13 | *.suo 14 | *.ntvs* 15 | *.njsproj 16 | *.sln 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-2018 Conanliu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 一个JavaScript-Obfuscator混淆逆向工具 2 | 3 | 目前支持对[JavaScript-Obfuscator](https://github.com/javascript-obfuscator/javascript-obfuscator.git)混淆后的字符串进行自动化还原。 4 | 5 | ## Usage 6 | 7 | **Notice: 本工具仅支持node 8及以上版本** 8 | 9 | ```shell 10 | # 安装依赖 11 | npm install 12 | # 读取当前目录的hello-world-obf.js并还原其字符串 13 | node index.js 14 | ``` 15 | 16 | ## 文件说明 17 | 18 | - hello-world-obf.js 经过`JavaScript-Obfuscator`混淆后的js,其功能是打印`hello world!` 19 | - index.js 逆向工具,自动读当前目录的`hello-world-obf.js`,并将逆向的产物输出到`dist`目录 20 | 21 | ## License 22 | 23 | MIT 24 | -------------------------------------------------------------------------------- /decrypt-str.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @author: conanliu 4 | * @date: 2018/6/2 5 | */ 6 | var window = {}; 7 | 8 | var b = function (arr, c, d) { 9 | c = c - 0x0; 10 | var e = arr[c]; 11 | if (b['WZDOeZ'] === undefined) { 12 | (function () { 13 | var f = function () { 14 | return window; 15 | }; 16 | var i = f(); 17 | var j = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 18 | window['atob'] = function (k) { 19 | var l = String(k)['replace'](/=+$/, ''); 20 | for (var m = 0x0, n, o, p = 0x0, q = ''; o = l['charAt'](p++); ~o && (n = m % 0x4 ? n * 0x40 + o : o, m++ % 0x4) ? q += String['fromCharCode'](0xff & n >> (-0x2 * m & 0x6)) : 0x0) { 21 | o = j['indexOf'](o); 22 | } 23 | return q; 24 | }; 25 | }()); 26 | var r = function (s, t) { 27 | var u = [], v = 0x0, w, x = '', y = ''; 28 | s = window.atob(s); 29 | for (var z = 0x0, A = s['length']; z < A; z++) { 30 | y += '%' + ('00' + s['charCodeAt'](z)['toString'](0x10))['slice'](-0x2); 31 | } 32 | s = decodeURIComponent(y); 33 | for (var B = 0x0; B < 0x100; B++) { 34 | u[B] = B; 35 | } 36 | for (B = 0x0; B < 0x100; B++) { 37 | v = (v + u[B] + t['charCodeAt'](B % t['length'])) % 0x100; 38 | w = u[B]; 39 | u[B] = u[v]; 40 | u[v] = w; 41 | } 42 | B = 0x0; 43 | v = 0x0; 44 | for (var C = 0x0; C < s['length']; C++) { 45 | B = (B + 0x1) % 0x100; 46 | v = (v + u[B]) % 0x100; 47 | w = u[B]; 48 | u[B] = u[v]; 49 | u[v] = w; 50 | x += String['fromCharCode'](s['charCodeAt'](C) ^ u[(u[B] + u[v]) % 0x100]); 51 | } 52 | return x; 53 | }; 54 | b['KQUfKj'] = r; 55 | b['uJDXUo'] = {}; 56 | b['WZDOeZ'] = !![]; 57 | } 58 | var D = b['uJDXUo'][c]; 59 | if (D === undefined) { 60 | if (b['yKBrRW'] === undefined) { 61 | b['yKBrRW'] = !![]; 62 | } 63 | e = b['KQUfKj'](e, d); 64 | b['uJDXUo'][c] = e; 65 | } else { 66 | e = D; 67 | } 68 | return e; 69 | }; 70 | 71 | module.exports = b; 72 | -------------------------------------------------------------------------------- /dist/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conanliu/de-js-obfuscator/eefc31d8d9a1da675accf10440bbb366c945c5d2/dist/.gitkeep -------------------------------------------------------------------------------- /hello-world-obf.js: -------------------------------------------------------------------------------- 1 | var _0xec4b=['WsO5wq8=','N0IucQ==','dWtURyY=','wrXDtWHCkg==','w5A0wpU4Ig==','w4x/w7LDnAzDlSADw7Y=','wppPE3fDpQ==','wrBww6U=','DzvCocO8wpovw4g=','wq8nIcKh','fsOLwq5Ow5pcw7I=','w4DCqcO9w5w/','w67CksOnVsKXwoNG','w74vYsK9','w47DoSHDpUw3w4o=','wqvCgcOwwr9v','bcOdFjJ7w4jDog==','LAZkw4wtw4DCl8KgUA==','w74Iw77DqMKuHg0=','NTYHFsO2','w4HDoSg=','w75gwpvCm8Oew5NgwpBWwo7CscK5','aAtRw40O','b8OBwrRIw4dewrfCjV3Cr0w3O1jCv8OtfCHCnw==','OjlIFsO8BhceLMOOY3lhwrrCosKUwoTCk8O3wqIpw5rCiEp8wpxrwrkqRVEL','VcO5wqbDsznCosOb','XMKfwovDnyLChTs='];(function(_0x1ba5e0,_0x5e07d2){var _0x19326b=function(_0x4ee438){while(--_0x4ee438){_0x1ba5e0['push'](_0x1ba5e0['shift']());}};_0x19326b(++_0x5e07d2);}(_0xec4b,0x1ab));var _0x4547=function(_0x33fe76,_0x3a47b7){_0x33fe76=_0x33fe76-0x0;var _0x4eb3a0=_0xec4b[_0x33fe76];if(_0x4547['vFCZKL']===undefined){(function(){var _0x5703b9;try{var _0x48cf09=Function('return\x20(function()\x20'+'{}.constructor(\x22return\x20this\x22)(\x20)'+');');_0x5703b9=_0x48cf09();}catch(_0x1f0136){_0x5703b9=window;}var _0x215a9d='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x5703b9['atob']||(_0x5703b9['atob']=function(_0x393bad){var _0x3e85ee=String(_0x393bad)['replace'](/=+$/,'');for(var _0x4199ad=0x0,_0x2a3e75,_0x542040,_0x3c9b0c=0x0,_0x5df78c='';_0x542040=_0x3e85ee['charAt'](_0x3c9b0c++);~_0x542040&&(_0x2a3e75=_0x4199ad%0x4?_0x2a3e75*0x40+_0x542040:_0x542040,_0x4199ad++%0x4)?_0x5df78c+=String['fromCharCode'](0xff&_0x2a3e75>>(-0x2*_0x4199ad&0x6)):0x0){_0x542040=_0x215a9d['indexOf'](_0x542040);}return _0x5df78c;});}());var _0x475500=function(_0x1efe4f,_0x1015e9){var _0x42f833=[],_0x1c291c=0x0,_0x65a60d,_0x4380ba='',_0x5e5782='';_0x1efe4f=atob(_0x1efe4f);for(var _0x1eeaca=0x0,_0x5c7e18=_0x1efe4f['length'];_0x1eeaca<_0x5c7e18;_0x1eeaca++){_0x5e5782+='%'+('00'+_0x1efe4f['charCodeAt'](_0x1eeaca)['toString'](0x10))['slice'](-0x2);}_0x1efe4f=decodeURIComponent(_0x5e5782);for(var _0x264db7=0x0;_0x264db7<0x100;_0x264db7++){_0x42f833[_0x264db7]=_0x264db7;}for(_0x264db7=0x0;_0x264db7<0x100;_0x264db7++){_0x1c291c=(_0x1c291c+_0x42f833[_0x264db7]+_0x1015e9['charCodeAt'](_0x264db7%_0x1015e9['length']))%0x100;_0x65a60d=_0x42f833[_0x264db7];_0x42f833[_0x264db7]=_0x42f833[_0x1c291c];_0x42f833[_0x1c291c]=_0x65a60d;}_0x264db7=0x0;_0x1c291c=0x0;for(var _0x55bc01=0x0;_0x55bc01<_0x1efe4f['length'];_0x55bc01++){_0x264db7=(_0x264db7+0x1)%0x100;_0x1c291c=(_0x1c291c+_0x42f833[_0x264db7])%0x100;_0x65a60d=_0x42f833[_0x264db7];_0x42f833[_0x264db7]=_0x42f833[_0x1c291c];_0x42f833[_0x1c291c]=_0x65a60d;_0x4380ba+=String['fromCharCode'](_0x1efe4f['charCodeAt'](_0x55bc01)^_0x42f833[(_0x42f833[_0x264db7]+_0x42f833[_0x1c291c])%0x100]);}return _0x4380ba;};_0x4547['kJAaAj']=_0x475500;_0x4547['twEDBc']={};_0x4547['vFCZKL']=!![];}var _0x254298=_0x4547['twEDBc'][_0x33fe76];if(_0x254298===undefined){if(_0x4547['JSVisK']===undefined){_0x4547['JSVisK']=!![];}_0x4eb3a0=_0x4547['kJAaAj'](_0x4eb3a0,_0x3a47b7);_0x4547['twEDBc'][_0x33fe76]=_0x4eb3a0;}else{_0x4eb3a0=_0x254298;}return _0x4eb3a0;};function foo(){var _0x538d25=function(){var _0x140387=!![];return function(_0x116291,_0x24a1d6){var _0x41e351=_0x140387?function(){if(_0x24a1d6){var _0x39fb6d=_0x24a1d6[_0x4547('0x0','@Km5')](_0x116291,arguments);_0x24a1d6=null;return _0x39fb6d;}}:function(){};_0x140387=![];return _0x41e351;};}();var _0x430483=_0x538d25(this,function(){var _0xe23353=function(){};var _0x385585;try{var _0x220982=Function(_0x4547('0x1','OGsb')+_0x4547('0x2','I@CV')+');');_0x385585=_0x220982();}catch(_0x43635b){_0x385585=window;}if(!_0x385585[_0x4547('0x3','D2vH')]){_0x385585[_0x4547('0x4','T3hw')]=function(_0x5b0edb){var _0x36982a={};_0x36982a[_0x4547('0x5','D2vH')]=_0x5b0edb;_0x36982a[_0x4547('0x6',']H%n')]=_0x5b0edb;_0x36982a[_0x4547('0x7','pwED')]=_0x5b0edb;_0x36982a[_0x4547('0x8','iKd3')]=_0x5b0edb;_0x36982a[_0x4547('0x9','ZLJY')]=_0x5b0edb;_0x36982a[_0x4547('0xa','bHox')]=_0x5b0edb;_0x36982a[_0x4547('0xb','0qvQ')]=_0x5b0edb;return _0x36982a;}(_0xe23353);}else{_0x385585[_0x4547('0x4','T3hw')][_0x4547('0xc','SxC(')]=_0xe23353;_0x385585[_0x4547('0xd','jBTa')][_0x4547('0xe','eVCR')]=_0xe23353;_0x385585[_0x4547('0xf','OGsb')][_0x4547('0x10','(oqV')]=_0xe23353;_0x385585[_0x4547('0x11','$E!2')][_0x4547('0x12','JfHT')]=_0xe23353;_0x385585[_0x4547('0x13','^ga*')][_0x4547('0x14','5]7g')]=_0xe23353;_0x385585[_0x4547('0x15','%R3o')][_0x4547('0x16','pJcd')]=_0xe23353;_0x385585[_0x4547('0x17','ZkXT')][_0x4547('0x18','I@CV')]=_0xe23353;}});_0x430483();console[_0x4547('0x19','^ga*')](_0x4547('0x1a','!m#D'));}foo(); 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * @author: conanliu 5 | * @date: 2018/5/25 6 | */ 7 | 8 | const fs = require('fs'); 9 | const UglifyJS = require('uglify-js'); 10 | const ds = require('./decrypt-str'); 11 | 12 | function decString(code) { 13 | // replace all encoded string 14 | code = code.replace(/'\\x.*?'/g, function ($0) { 15 | return `'${eval($0).replace(/'/g, "\\'")}'`; 16 | }).replace(/'\\u.*?'/g, function ($0) { 17 | return `'${eval($0).replace(/'/g, "\\'")}'`; 18 | }); 19 | 20 | // string array offset 21 | const offset = parseInt(/,(0x[0-9a-f]+)\)/.exec(code)[1], 16); 22 | // find the function name of decrypt string 23 | const decStrFnName = /\b(\w+)\('0x0'[,)]{1}.*?\1\('0x1'[,)]{1}/.exec(code)[1]; 24 | // string array 25 | const stringArray = eval(/var \w+=(\['(.*?)'(,'(.*?)')*?])/g.exec(code)[1]); 26 | 27 | const decFnsRegex = `${decStrFnName}\\('(0x.+?)'(,'(.*?)')?\\)`; 28 | 29 | function recoveryArrayOrder(stringArr, offset) { 30 | const foo = function (i) { 31 | while (--i) { 32 | stringArr['push'](stringArr['shift']()); 33 | } 34 | }; 35 | foo(++offset); 36 | } 37 | recoveryArrayOrder(stringArray, offset); 38 | 39 | const decryptFnsReg = new RegExp(decFnsRegex, 'g'); 40 | const decryptFns = code.match(decryptFnsReg); 41 | const rawStr = []; 42 | const decStrMap = {}; 43 | for (let i = 0; i < decryptFns.length; i++) { 44 | const item = decryptFns[i]; 45 | const pair = new RegExp(decFnsRegex).exec(item); 46 | const index = +pair[1]; 47 | if (pair[3]) { 48 | rawStr[index] = ds(stringArray, pair[1], pair[3]); 49 | } else { 50 | rawStr[index] = stringArray[+pair[1]]; 51 | } 52 | 53 | decStrMap[stringArray[index]] = rawStr[index]; 54 | } 55 | 56 | // replace in code 57 | const deobfCode = code.replace(new RegExp(decFnsRegex, 'g'), function ($0, $1, $2) { 58 | return `'${(rawStr[+$1] || '').replace(/'/g, "\\'")}'`; 59 | }).replace(/'(.*?)'/g, function ($0, $1) { 60 | const raw = decStrMap[$1]; 61 | if (raw && typeof raw === 'string') return `'${raw.replace(/'/g, "\\'")}'`; 62 | return $0; 63 | }); 64 | 65 | return deobfCode; 66 | } 67 | 68 | // read code 69 | let code = fs.readFileSync('./hello-world-obf.js', 'utf8'); 70 | 71 | // write to file 72 | fs.writeFileSync(`./dist/${+new Date()}.js`, UglifyJS.parse(decString(code)).print_to_string({ 73 | beautify: true, 74 | }), 'utf8'); 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "de-js-obfuscator", 3 | "version": "0.0.1", 4 | "description": "javascript obfuscator decrypt tool.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "javascript-obfuscator" 11 | ], 12 | "author": "conanliu", 13 | "license": "MIT", 14 | "dependencies": { 15 | "uglify-js": "^3.4.9" 16 | } 17 | } 18 | --------------------------------------------------------------------------------