像軟件加密與解密同樣,javascript的混淆與解混淆同屬於同一個範疇。道高一尺,魔高一丈。沒有永恆的黑,也沒有永恆的白。一切都是資本市場驅動行爲,如今都流行你能爲人解決什麼問題,這個概念。那麼市場究竟能容納多少個能解決這種問題的利益者。JS沒有祕密。javascript
其實本人不同意javascript進行hash混淆處理,一拖慢運行時速度,二體積大。JS代碼前端可獲取,天生賦予「開源」屬性,均可以在chrome devTools下查看。JS非壓縮性混淆徹底違法前端優化準則。css
eval混淆,也是最先JS出現的混淆加密,聽說第一天就被破解,修改一下代碼,alert一下就能夠破解了。這種方法從出生的那天就失去了意義。其實JS加密(混淆)是相對於可讀性而言的,其實真正有意義的就是壓縮型混淆uglify這一類,便可減小體重,也可減小可讀性。html
可是,也不能排除部分商業源代碼使用hash類型混淆源代碼,好比 miniui 使用的JSA加密, fundebug使用的javascript-obfuscator。前端
下面經過代碼來講明 JSA加密 和 javascript-obfuscator 的區別:vue
要混淆的代碼:java
function logG(message) { console.log('\x1b[32m%s\x1b[0m', message); } function logR(message) { console.log('\x1b[41m%s\x1b[0m', message); } logG('logR'); logR('logG');
經過JSA加密混淆後生成的代碼react
function o00($){console.log("\x1b[32m%s\x1b[0m",$)}function o01($){console.log("\x1b[41m%s\x1b[0m",$)}o00("logR");o01("logG")
而後再beautifier一下:webpack
function o00($) { console.log("\x1b[32m%s\x1b[0m", $) } function o01($) { console.log("\x1b[41m%s\x1b[0m", $) } o00("logR"); o01("logG")
能夠發現,其實沒有作什麼什麼修改,只是作了一些變量替換。想還原也比較簡單的。這裏就不拿它來作表明,也沒有什麼人用。git
經過javascript-obfuscator混淆後生成的代碼es6
var _0xd6ac=['[41m%s[0m','logG','log'];(function(_0x203a66,_0x6dd4f4){var _0x3c5c81=function(_0x4f427c){while(--_0x4f427c){_0x203a66['push'](_0x203a66['shift']());}};_0x3c5c81(++_0x6dd4f4);}(_0xd6ac,0x6e));var _0x5b26=function(_0x2d8f05,_0x4b81bb){_0x2d8f05=_0x2d8f05-0x0;var _0x4d74cb=_0xd6ac[_0x2d8f05];return _0x4d74cb;};function logG(_0x4f1daa){console[_0x5b26('0x0')]('[32m%s[0m',_0x4f1daa);}function logR(_0x38b325){console[_0x5b26('0x0')](_0x5b26('0x1'),_0x38b325);}logG('logR');logR(_0x5b26('0x2'));
再beautifier一下:
var _0xd6ac = ['[41m%s[0m', 'logG', 'log']; (function(_0x203a66, _0x6dd4f4) { var _0x3c5c81 = function(_0x4f427c) { while (--_0x4f427c) { _0x203a66['push'](_0x203a66['shift']()); } }; _0x3c5c81(++_0x6dd4f4); }(_0xd6ac, 0x6e)); var _0x5b26 = function(_0x2d8f05, _0x4b81bb) { _0x2d8f05 = _0x2d8f05 - 0x0; var _0x4d74cb = _0xd6ac[_0x2d8f05]; return _0x4d74cb; }; function logG(_0x4f1daa) { console[_0x5b26('0x0')]('[32m%s[0m', _0x4f1daa); } function logR(_0x38b325) { console[_0x5b26('0x0')](_0x5b26('0x1'), _0x38b325); } logG('logR'); logR(_0x5b26('0x2'));
這個複雜得多,可是分析一下你會發現,其實多了一個字典,全部方法變量,都有可能存在字典中,調用時先調用字典還原方法名變量再執行。
其實入口都是變量的規則。
字典函數:
var _0xd6ac = ['[41m%s[0m', 'logG', 'log']; (function(_0x203a66, _0x6dd4f4) { var _0x3c5c81 = function(_0x4f427c) { while (--_0x4f427c) { _0x203a66['push'](_0x203a66['shift']()); } }; _0x3c5c81(++_0x6dd4f4); }(_0xd6ac, 0x6e)); var _0x5b26 = function(_0x2d8f05, _0x4b81bb) { _0x2d8f05 = _0x2d8f05 - 0x0; var _0x4d74cb = _0xd6ac[_0x2d8f05]; return _0x4d74cb; };
經過以上發現,咱們能夠把JS混淆歸結爲三類,分別是 eval類型,hash類型,壓縮類型。而壓縮類型,是目前前端性能優化的經常使用工具,以uglify爲表明。
JavaScript:
CSS:
HTML:
從工具流(workflow) 來看,不管是 webpack 仍是 gulp ,目前javascript最流行工具仍是uglify。
解混淆策略實際上是依據生成代碼規律編寫,不外乎觀察特徵分析,再觀察特徵分析,不斷調整。都是手辦眼見功夫。
都沒有什麼難度可言,有的就是耐性。好比javascript-obfuscator對應的解混淆工具能夠
分解爲N因子問題:
如何查詢function的做用域?
預執行變量替換可能存在類型?
...
如:
var _0xd6ac = ['[41m%s[0m', 'logG', 'log']; (function(_0x203a66, _0x6dd4f4) { var _0x3c5c81 = function(_0x4f427c) { while (--_0x4f427c) { _0x203a66['push'](_0x203a66['shift']()); } }; _0x3c5c81(++_0x6dd4f4); }(_0xd6ac, 0x6e)); var _0x5b26 = function(_0x2d8f05, _0x4b81bb) { _0x2d8f05 = _0x2d8f05 - 0x0; var _0x4d74cb = _0xd6ac[_0x2d8f05]; return _0x4d74cb; }; function logG(_0x4f1daa) { console[_0x5b26('0x0')]('[32m%s[0m', _0x4f1daa); } function logR(_0x38b325) { console[_0x5b26('0x0')](_0x5b26('0x1'), _0x38b325); } logG('logR'); logR(_0x5b26('0x2'));
要還原成
function logG(message) { console.log('\x1b[32m%s\x1b[0m', message); } function logR(message) { console.log('\x1b[41m%s\x1b[0m', message); } logG('logR'); logR('logG');
第一步你總得知道字典函數,而後執行字典函數 _0x5b26('0x0')
還原成 log
.
那麼就好辦了,寫代碼的事。
如 https://github.com/jscck/crack.js/blob/master/crack.js
還原後,如何重構代碼,那麼你還得知道代碼生成以前是經過什麼工具打包的webpack? 仍是?
如webpack 的各類封裝頭和尾
https://webpack.js.org/config...
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports['MyLibrary'] = factory(); else root['MyLibrary'] = factory(); })(typeof self !== 'undefined' ? self : this, function() { return _entry_return_; });
假如再深刻一點,可能會涉及到JS語法解釋器, AST抽象語法樹
目前涉及到 JS語法解釋器, AST抽象語法樹的功能以下:
或者能夠閱讀《編程語言實現模式》,涉及到 antlr4。
固然也能夠經過esprima等工具來作解混淆,只是工做量大一點,值不值的問題。
對於將來,JS商業源碼加密的方向可能webassembly,先在服務端編譯成wasm,源碼就能真正的閉源。
有人的地方就有路,有混淆的地方就有解混淆,目前機器學習編程響應的解混淆工具也作的至關出色,好比
Machine Learning for Programming 產品
nice2predict,jsnice ...
查看 https://www.sri.inf.ethz.ch/r...
爲何額外說一下AST抽象語法樹,由於你能夠 input-> ast -> output Anything。
好比你jsx轉換小程序模版語法,這樣你就能夠用react語法來寫小程序,如Taro。
mpvue, wepy, postcss …… 這些都是經過AST進行構建轉換的工具,es6 -> es5, babel 都是使用AST。
AST抽象語法樹大體流程:
Input 生成 AST tree
而後經過AST類型斷言進行相應的轉換
http://esprima.org/demo/parse...
小程序
https://github.com/qwerty4721...
推薦.Net、C# 逆向反編譯四大工具利器
https://www.cnblogs.com/ldc21...
2018年支持java8的Java反編譯工具彙總
https://blog.csdn.net/yannqi/...