讲OB混淆前,先要讲讲什么是混淆,混淆就是在不影响原本的代码的逻辑情况下对代码的形式和结构进行变动,使代码变得难以阅读和理解,增加代码逆向的难度。
在JavaScript中,OB混淆一般是指的网站 https://obfuscator.io 提供的代码混淆,这里面提供了一个JavaScript代码混淆工具,可进行多种类型的代码混淆。
OB混淆识别
OB混淆基本上由四个部分组成:
1 2 3 4
| - 一个大数组或者有一个大数组的函数 - 一个自执行函数 - 解密函数 - 加密后的函数
|
这是OB混淆代码的基本构成,OB混淆有如下识别特征:
1 2 3 4 5 6 7 8 9 10 11
| var _0xabcd = (function() { var _0x1 = function(_0x2, _0x3) { return _0x1234[_0x2 - 0x0]; }; _0x1['XyzAbc'] = function(_0x4, _0x5) { return atob(_0x4); }; return _0x1; }());
|
纸上得来终觉浅,动手试一试才能知道OB混淆前后的改动:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| !function(e){ console.log("Hello ", e); }("Maododo")
function _0x6cd1(_0x2fe9d3, _0x578f74) { _0x2fe9d3 = _0x2fe9d3 - 0xd0; var _0x4838f2 = _0x4838(); var _0x6cd151 = _0x4838f2[_0x2fe9d3]; return _0x6cd151; }
var _0x158908 = _0x6cd1;
function _0x4838() { var _0xce77dd = ['3856821yyeMCS', '5493156EGSXls', 'Hello\x20', 'Maododo', '346717LaHyhs', '39063eTpdiF', '7841840ZMbEsb', '4810420OBpuHU', '52ZUnfxK', '128SJfwCf', '17811900VqAsEG']; _0x4838 = function () { return _0xce77dd; }; return _0x4838(); }
(function (_0x122f90, _0x42566e) { var _0x5937d2 = _0x6cd1, _0x219585 = _0x122f90(); while (!![]) { try { var _0x28d2fe = -parseInt(_0x5937d2(0xd5)) / 0x1 * (parseInt(_0x5937d2(0xd8)) / 0x2) + parseInt(_0x5937d2(0xd0)) / 0x3 + parseInt(_0x5937d2(0xd7)) / 0x4 + parseInt(_0x5937d2(0xd6)) / 0x5 + -parseInt(_0x5937d2(0xd1)) / 0x6 + -parseInt(_0x5937d2(0xd4)) / 0x7 * (-parseInt(_0x5937d2(0xd9)) / 0x8) + -parseInt(_0x5937d2(0xda)) / 0x9; if (_0x28d2fe === _0x42566e) break; else _0x219585['push'](_0x219585['shift']()); } catch (_0x342f06) { _0x219585['push'](_0x219585['shift']()); } } }(_0x4838, 0xe533c), !function (_0x44a1d9) { var _0x355098 = _0x6cd1; console['log'](_0x355098(0xd2), _0x44a1d9); }(_0x158908(0xd3)));
|
上面的代码就是一个标准的四个模块,让我来给拆解一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| function _0x6cd1(_0x2fe9d3, _0x578f74) { _0x2fe9d3 = _0x2fe9d3 - 0xd0; var _0x4838f2 = _0x4838(); var _0x6cd151 = _0x4838f2[_0x2fe9d3]; return _0x6cd151; }
var _0x158908 = _0x6cd1;
function _0x4838() { var _0xce77dd = ['3856821yyeMCS', '5493156EGSXls', 'Hello\x20', 'Maododo', '346717LaHyhs', '39063eTpdiF', '7841840ZMbEsb', '4810420OBpuHU', '52ZUnfxK', '128SJfwCf', '17811900VqAsEG']; _0x4838 = function () { return _0xce77dd; }; return _0x4838(); }
(function (_0x122f90, _0x42566e) { var _0x5937d2 = _0x6cd1, _0x219585 = _0x122f90(); while (!![]) { try { var _0x28d2fe = -parseInt(_0x5937d2(0xd5)) / 0x1 * (parseInt(_0x5937d2(0xd8)) / 0x2) + parseInt(_0x5937d2(0xd0)) / 0x3 + parseInt(_0x5937d2(0xd7)) / 0x4 + parseInt(_0x5937d2(0xd6)) / 0x5 + -parseInt(_0x5937d2(0xd1)) / 0x6 + -parseInt(_0x5937d2(0xd4)) / 0x7 * (-parseInt(_0x5937d2(0xd9)) / 0x8) + -parseInt(_0x5937d2(0xda)) / 0x9; if (_0x28d2fe === _0x42566e) break; else _0x219585['push'](_0x219585['shift']()); } catch (_0x342f06) { _0x219585['push'](_0x219585['shift']()); } } }(_0x4838, 0xe533c), !function (_0x44a1d9) { var _0x355098 = _0x6cd1; console['log'](_0x355098(0xd2), _0x44a1d9); }(_0x158908(0xd3)));
|
如何处理OB混淆?
解OB混淆工具、网站
既然有工具能够直接生成OB混淆,那也有工具能直接解OB混淆
https://thanhle.io.vn/de4js/
https://github.com/Tsaiboss/decodeObfuscator
这个工具整一下午没整好,有点失望,我后续将补充这部分内容,暂且搁置。
手动补OB混淆环境
这里就跟OB混淆关系不是很大了,只需要将OB混淆后的部分,全部copy下来,简单替换几个函数就可以了,主要是耐心,
例子
OB混淆只不过是混淆之万一,用的多了,将规律记录下来,下次遇到便会得心应手。
某公众号功能

这里用的是真机adb调试Chrome inspect调试,针对不能开启f12的公众号、内嵌webview的APP还是挺好用的,公众号使用的话,还需要在微信输入并点击:
1
| http://debugxweb.qq.com/?inspector=true
|

然后就可以进去直接调试,还需要再绕过一下无限debugger,这个网上文章有很多,这里用的是这个修改原型链:
1 2 3 4 5 6 7 8
| var _constructor = constructor; Function.prototype.constructor = function(s) { if (s == "debugger"){ console.log(s); return null; } return _constructor(s); }
|

可以发现这里,请求荷载被加密了,打断点跟一下,看看在哪里处理加密解密的

来看看这个函数,使用了经典的平坦流控制进行关键函数的混淆,也就是while true 和 switch 进行代码执行流程的混淆,只需要打断点,然后把执行的代码,一行一行拼一起,不过我们只在乎请求报文加密,这里可以忽视。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| function encryptParamNEW(_$H, _$l, _$n) { var Hg = a053b76H5; var _$W = { 'Hbpen': Hg(0x1f1), 'ZFdMc': function(_$U, _$h) { return _$U === _$h; }, 'XFqac': function(_$U, _$h) { return _$U(_$h); }, 'LFjsF': function(_$U, _$h) { return _$U + _$h; }, 'fgiYN': function(_$U, _$h) { return _$U * _$h; }, 'EeZUw': function(_$U, _$h, _$L) { return _$U(_$h, _$L); } }; var _$c = _$W[Hg(0x239)][Hg(0x296)]('|'); var _$w = 0x0; while (!![]) { switch (_$c[_$w++]) { case '0': if (_$W[Hg(0x307)](_$n, '1')) { _$W[Hg(0x226)](encryptJSONSM, _$H); } else { var _$D = _$W['XFqac'](parseInt, _$W[Hg(0x245)](_$W[Hg(0x25f)](0x55d4a7f, Math[Hg(0x2fa)]()), 0x989680)) + ''; _$H = _$W[Hg(0x226)](wrapParam, _$H); _$W['EeZUw'](encryptJSON, _$H, _$D); } continue; case '1': _$l['macKey'] = _$T; continue; case '2': var _$T = ''; continue; case '3': var _$P = encryptParam(_$H); continue; case '4': return _$H; case '5': _$T = JSON['parse'](_$P[Hg(0x305)]).mac; continue; } break; } }
|
简单看一下,这个函数是用来加mac的,进行报文防篡改的,可以pass了。


很清楚的看到,报文被加密了,这个ob混淆很简单呢,加密函数一下就能找到,也不用处理大数组等东西,算是一个失败的例子。
然后再进行代码本地化。

补环境过程中,遇到这种本地不能被解析的,可以直接去调试,看看这里是什么,直接替换成明文。因为这个getPublicKey函数比较短,环境不变的情况下,一般只有一个返回值,这里就可以自行判断,只留下return的值,也是个不错的补环境方法。
其中有一个反调试函数,可以尝试将它删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| function _$o(_$H) { var l1 = a053b76H5; var _$l = { 'vtNxu': function(_$W, _$c) { return _$W + _$c; }, 'wBNvU': function(_$W, _$c) { return _$W > _$c; }, 'vWIAu': l1(0x22b), 'wetVp': l1(0x22a), 'SIxJk': function(_$W, _$c) { return _$W !== _$c; }, 'BFLGu': function(_$W, _$c) { return _$W / _$c; }, 'Kjmsr': l1(0x2cc), 'PgkEJ': function(_$W, _$c) { return _$W + _$c; }, 'cjBVC': l1(0x2f1), 'GBnkh': l1(0x273), 'IuCnL': l1(0x259), 'YwJsb': function(_$W, _$c) { return _$W > _$c; } }; function _$n(_$W) {
} try { if (_$H) { return _$n; } else { _$n(0x0); } } catch (_$W) {} }
(function() { var g = a053b76n; var _$H = { 'iwjOC': function(_$W, _$c) { return _$W(_$c); }, 'tSvXd': function(_$W, _$c) { return _$W + _$c; } }; var _$l = function() { var K = a053b76n; var _$W; try { _$W = _$H[K(0x228)](Function, _$H[K(0x22e)](K(0x31b) + '{}.constructor(\x22return\x20this\x22)(\x20)', ');'))(); } catch (_$c) { _$W = typeof window !== K(0x2ac) ? window : this; } return _$W; }; var _$n = _$l(); _$n[g(0x204)](_$o, 0xfa0); }());
|
将这两个部分的代码注释掉就可以,接下来就是很基础的补环境,还有ob混淆的补环境。大体就是这个流程。
