JS逆向:拨云见日,AST解混淆让代码分析变得如此简单
本文分析的是某国外的cdn,如果国内有使用该cdn的站点,请与本人联系立即删除,谢谢。
遵纪守法是每个公民应尽的义务和责任。
要分析的网站如下:
"aHR0cHM6Ly9ib29raW5nLnZvbG90ZWEuY29tLw=="
需要着重分析的是类似下面的url:
"aHR0cHM6Ly9ib29raW5nLnZvbG90ZWEuY29tL19JbmNhcHN1bGFfUmVzb3VyY2U/U1dKSVlMV0E9NzE5ZDM0ZDMxYzhlM2E2ZTZmZmZkNDI1ZjdlMDMyZjMmbnM9MSZjYj0xNjc2OTgyNzkx"
它返回的是一段JavaScript代码:
由于代码太长,我就放个截图吧,它是一个自执行的函数,首先定义了很长的一串字符串,然后再进行 String.fromCharCode操作,最后eval运行,会生成一个 ___utmvc 字段的cookie,你可以在这个页面看到它:
https://booking.volotea.com/_Incapsula_Resource?SWKMTFSR=1&e=0.2468114615986583
在这里将 此时的cookie值贴出来吧,方便后续比对:
/oA8Rt/lbDWHcnO+WZ9if+CgW5rbtdD3irqpowAoj5MJToS4myQS2GcV9HW6xuOedImC3ad9KdD+i/yBIlqIYg70N55pPqfjMGZxG14QELhFn6o/KiGkX6A2452xoqbG5wvQxvkygVIoNV/mGJNTPgTQquXxlJ7cyjRmTHxteDWrFTf6A+y5sOeARNLKCMSlhDTYIpYcvI8wajq8Z4h4Xbtkoz+JUehzO+oO+z7UmIihfVig/d/Pz0PHOLDyRHVBwWG/GCkBf7oYGVPJF2xCncm1KA88e5Et6YAhusA2NJq0gA4Yrg+Vqky3roJp2lbPZVyaOSQ34QFf32mxTkgIwWC4esVHANil2vwuIckM/L6HA+xqZQTyB9Br8fGdD/21oY7g/U6iAaxU+sJGnvIPRqz7nDTPOdgyDF+HA1FqJ8O0fDtsi9JEHb+ZJ6waVGYGg9U8Mq2dyj3iPgqevNOK8X/mHojutQlRIkWlzsvANvq7KI03YaBsKEKPdzM5o5//Y7VI0t7lOSHpCep5cIMyM7LbT/8CHumKzPfDpXiLbeV/Q7YvhXOuDX61VZ25MVxnC2YGVj4AIiTLL1qRwsmMqIZHcLu0X3/svtrWNuFvjlP7ia2NffWHZegGDfz++oodPVOIqP9HVnKvYtXSt5MtDtAMb/cSr5TLmIamo5LdXqokFZBVwur6kg27YXAI9Xf5nD6CdScdczAVVwJs1zfc7xZfOv82gxU1fn5Aby8eqr3TEc+JNLD+n7vPHdJSB30P12aEYciw/ngKV4J+yJqKRHFxmpO2GN3GotHq986P260rCmVxv0AuIVptdr9fXEa0mTXkGGsP/smC5zle3AqP/lHHvEsGrjqGsaInrC3S3rWEHgFsZOKyo6g3LTWGeIGwV/1WGsSzV3GvXlpZQNSqxmniqKNuJIXUQGm/HVFV6zg8ZJbOR0nTll5fYm2aGI/+2+52PMaK6wvO4FCTJme4/h2nd0byxGLIQz+3V7Lc7LWPVSuRwhVrtBGvP5X5lwqolwZADItxGjPkNfARgO25T5oeh2BxRvHpWF2Ufr4dJdiTy95KfKnx9fOfc3FATOK/ROBpyjDXFgcr+qAOl3/XNlqV5oXv1trw156LKN5dfps7tArFug7jPT9/lrRnLxwXAzKVWdV6RCmV3l2DjYcsw4qMlUlh6Tbcwvun7nBKS1xyVyFTaUeb7Yw0ZiOEV9BwlkJFRMNf8CiPxUcbhojPFk+YoDnhzxGKj9TaQvNZeQpQHBTJSNrWiaXCFyb9Cr3Gheg4JE111jRZyOmGEQuJLcoEULMxRWA84iE3AIu9h9n1UlCF/K64LyMuQemHwwksSMffux/LPrCs+2V3XwKq8uNjejkvZ2oKR0E8nwSh/4eVZ2oqYbv54V8wl2Rn19gTRkf5s/ADqkIyyQd6XMigAlBO9CYA7wA+TpZmXDd+kN/PyPDscDmeW0Ade1J++e3jjw+6XJZCBpaG88rJevJx64F+VmclgZ4ahRzEEw10vhUjKVf1Lf9gxM8QEtuyS6tgPjYTQmPjLt2kR3MfDtc0meMDuWQS4HJcWLAmlM8A/iEo/0+kISFCOWba6XgCS43N+HUuqCa9yFtUfXTIqtumYAvvtCxkaWdlc3Q9MTEyNzk0LHM9ODQ4OTdhYWU5ZjgyYTg4MjdhOGE4OTc5ODE5ZTgyNjc4ZDlhNzU3M2E1NzBhZTZiNjVhMzc2NzlhNDg4NjBhMzdiYWRhZDZiN2Y3MjZmNzQ=
如图:
将这段源代码复制到浏览器控制台,把eval 换成 console.log,回车,再把结果美化下,如图:
很多很多这样的十六进制字符串,用之前介绍的方法,将其转换为 人能识别的字符串,还原后成这样:
又变成了我们非常熟悉的ob混淆了,这种代码之前有介绍过怎么处理,通过操作AST将加密的函数调用解密成字符串:
这比之前的代码又要清爽很多,我们注意到有几个这样变量:
var _0x1df825 = {.....}
这种也是可以进行处理的,论坛上有相关的处理方法,请进行参考。处理后的部分代码:
同一位置 的截图如上,而这种 while--- switch语句也是可以通过操作AST来进行(请参考论坛上的文章)处理的,处理后的部分代码截图:
大致还原成这样就差不多了。我们在代码中直接搜索 ___utmvc 试试:
-
function _0x33d434(_0x62c6cd) {
-
var _0x2f2a79;
-
var _0x2cecf5 = _0x288ca0();
-
var _0x3dacec = new _0x2ca1a3["Array"](_0x2cecf5["length"]);
-
for (var _0x59635d = 0x0; _0x59635d < _0x2cecf5["length"]; _0x59635d++) {
-
_0x3dacec[_0x59635d] = _0x1efa58(_0x62c6cd + _0x2cecf5[_0x59635d]);
-
}
-
_0x50089f();
-
var _0x518d86 = "\x94\xF6|V\xF2\x87\xDA~";
-
var _0x5c5427 = _0x518d86["substr"](0x0, 0x5);
-
var _0x4116fd = _0x518d86["substr"](0x5);
-
var _0x39af56 = "\xEF\xF7q\x05\xE2\x12\xBE@";
-
var _0xeda3f4 = 0x3;
-
while (--_0xeda3f4) {
-
_0x39af56 = _0x39af56["substr"](0x1) + _0x39af56[0x0];
-
}
-
var _0xfd1c9f = _0x39af56;
-
var _0x3cc1e3 = _0x39af56["length"] - 0x1;
-
while (--_0x3cc1e3) {
-
_0xfd1c9f = _0xfd1c9f["substr"](0x1) + _0xfd1c9f[0x0];
-
}
-
var _0xf84116 = _0x27b9("0x65", _0x5c5427 + _0x4116fd);
-
var _0xca7a76 = _0x3dacec["join"]();
-
var _0x5bc98e = "";
-
for (var _0x59635d = 0x0; _0x59635d < _0xf84116["length"]; _0x59635d++) {
-
_0x5bc98e += (_0xf84116["charCodeAt"](_0x59635d) + _0xca7a76["charCodeAt"](_0x59635d % _0xca7a76["length"]))["toString"](0x10);
-
}
-
_0x50089f();
-
_0x7b92["push"](btoa(_0x62c6cd));
-
_0x2f2a79 = btoa(_0x27b9(_0x7b92["length"] - 0x1, _0xf84116["substr"](0x0, 0x5)) + ",digest=" + _0xca7a76 + ",s=" + _0x5bc98e);
-
_0x7b92["pop"]();
-
_0x28e982("___utmvc", _0x2f2a79, 0x14);
-
}
原来调用了 _0x28e982 这个函数:
-
function _0x28e982(_0x491bc3, _0x4bb0d1, _0x5dfe4c) {
-
var _0x35bb3c = "";
-
if (_0x5dfe4c) {
-
var _0x4a870b = new _0x2ca1a3["Date"]();
-
_0x4a870b["setTime"](_0x4a870b["getTime"]() + _0x5dfe4c * 0x3e8);
-
var _0x35bb3c = "; expires=" + _0x4a870b["toGMTString"]();
-
}
-
_0x240c09["cookie"] = _0x491bc3 + "=" + _0x4bb0d1 + _0x35bb3c + "; path=/";
-
}
逻辑很清晰,就是一个cookie赋值的功能,并且这个值是通过 参数传递进来的,也就是上面代码中的 _0x2f2a79,它是在这里赋值的:
_0x2f2a79 = btoa(_0x27b9(_0x7b92["length"] - 0x1, _0xf84116["substr"](0x0, 0x5)) + ",digest=" + _0xca7a76 + ",s=" + _0x5bc98e)
原来值是经过 base64编码的,我们将之前保存的cookie值解码看看:
以及:
解码后看到了 digest=112794, 以及
s=84897aae9f82a8827a8a8979819e82678d9a7573a570ae6b65a37679a48860a37badad6b7f726f74;
再一次佐证了生成的位置。前面是一段看不懂的字符串,我们从这个 digest 作为爆破点,它的值是 _0xca7a76,最后的生成位置在这里:
var _0xca7a76 = _0x3dacec["join"]();
继续往上追 _0x3dacec:
-
function _0x33d434(_0x62c6cd) {
-
var _0x2f2a79;
-
-
-
var _0x2cecf5 = _0x288ca0();
-
-
-
var _0x3dacec = new _0x2ca1a3["Array"](_0x2cecf5["length"]);
-
-
-
for (var _0x59635d = 0x0; _0x59635d < _0x2cecf5["length"]; _0x59635d++) {
-
_0x3dacec[_0x59635d] = _0x1efa58(_0x62c6cd + _0x2cecf5[_0x59635d]);
-
}
首先看 _0x288ca0 这个函数:
-
function _0x288ca0() {
-
var _0x1b9363 = new _0x2ca1a3["Array"]();
-
var _0x5f24c8 = new _0x2ca1a3["RegExp"]("^\\s?incap_ses_");
-
var _0x30d99e = _0x240c09["cookie"]["split"](";");
-
for (var _0x59e2f0 = 0x0; _0x59e2f0 < _0x30d99e["length"]; _0x59e2f0++) {
-
var _0x5c56a9 = _0x30d99e[_0x59e2f0]["substr"](0x0, _0x30d99e[_0x59e2f0]["indexOf"]("="));
-
var _0x727f65 = _0x30d99e[_0x59e2f0]["substr"](_0x30d99e[_0x59e2f0]["indexOf"]("=") + 0x1, _0x30d99e[_0x59e2f0]["length"]);
-
if (_0x5f24c8["test"](_0x5c56a9)) {
-
_0x1b9363[_0x1b9363["length"]] = _0x727f65;
-
}
-
}
-
_0x50089f();
-
return _0x1b9363;
-
}
是一段对 cookie操作的代码,这段代码拿到浏览器肯定是无法运行的,而且cookie的值早已改变,因此需要将这个响应是的cookie代码复制下来进行同样的操作:
我这里用的是火狐浏览器,因为我在谷歌浏览器上安装了插件,后面的代码有检测这个插件信息,会有干扰,处理起来不是很方便,因此我直接上一个没有插件的浏览器:
又因为直接在浏览器上进行cookie赋值,然后进行操作,会有错误:
这里只 split 出了一个字符串,肯定是不对的,我们直接搞一个普通的字符串split试试:
将这段代码替换进去即可:
-
function _0x288ca0() {
-
var _0x1b9363 = new _0x2ca1a3["Array"]();
-
var _0x5f24c8 = new _0x2ca1a3["RegExp"]("^\\s?incap_ses_");
-
var _0x30d99e = [ "dtSa=-", " dtCookie=1$E7FB92CBB7FBB1FC5FF8E30DD2DCE492|ea7c4b59f27d43eb|1", " ASP.NET_SessionId=4q44krz2ez3elmj0yzj33a3y", " geoInfo=%7b%22geoIp%22%3a2742941454%2c%22ip%22%3a%22163.125.247.14%22%2c%22continent%22%3a%22AS%22%2c%22country%22%3a%22CN%22%2c%22city%22%3a%22Shenzhen%22%2c%22currencyCode%22%3a%22CNY%22%2c%22displayCurrency%22%3a%22USD%22%2c%22latitude%22%3a22.5333%2c%22longitude%22%3a114.1333%2c%22nearestStation%22%3a%22ARK%22%2c%22selectedCurrency%22%3a%22USD%22%2c%22proposedCurrency%22%3a%22USD%22%2c%22multicurrency%22%3a%22true%22%7d", " tokenInfo=YTc2M2I2NmMtYWY5ZS00NTQ5LWI5ZTEtMjAyMDA1MTAxMDQzNDAtNHE0NGtyejJlejNlbG1qMHl6ajMzYTN5", " skysales=!Kq7SB+PO/C+ceuc8ac0SZ4cwGUzSo+xRgVJyWqQ7MsPUeBrSLwP47Kc3G60M05gvdg3Mzc6esR915ac=", " ak_bmsc=ED4CEB49C8564F5CFA51B7676AB804647D38DA39853B0000DCDAB75E9FE4491C~plWLTGb0mCa7xbnSj4xNL/P8qL4FEjWlb3wclYRkPpR6hTbMOBK+LLao1UcYIXYlNmX69xEtt8PfNm+Dz5i8+Yi6JXhzq1TME/Z3HFD4VhJQfJl7Qe7mRr6Sg3bVkFrGX3v07Ha7ZEkRSvVQ7RhvfL/kB8LcsZ26cri5aFVytSQFZqrhlaMA3zea2Awp1wToZM/2ROY/7wcfE+NcF5hOb1EWcZ8Fikj3XWfNH2yJLYmDo=", " visid_incap_1895301=Tt1zbPCESTK/r3VzH4f95trat14AAAAAQUIPAAAAAABVDXqy6Jps1PJU7XM/v/8h", " nlbi_1895301=BNMkF3yZniMb8R43DWaj5AAAAAD/pJINRErKpKVBTRoo1P4k", " incap_ses_895_1895301=2/MZXcUNA3/iTzGHy65rDNzat14AAAAAi5FxnRPDlgGDDChRiyp6WA==" ];
-
for (var _0x59e2f0 = 0x0; _0x59e2f0 < _0x30d99e["length"]; _0x59e2f0++) {
-
var _0x5c56a9 = _0x30d99e[_0x59e2f0]["substr"](0x0, _0x30d99e[_0x59e2f0]["indexOf"]("="));
-
-
-
var _0x727f65 = _0x30d99e[_0x59e2f0]["substr"](_0x30d99e[_0x59e2f0]["indexOf"]("=") + 0x1, _0x30d99e[_0x59e2f0]["length"]);
-
-
-
if (_0x5f24c8["test"](_0x5c56a9)) {
-
_0x1b9363[_0x1b9363["length"]] = _0x727f65;
-
}
-
}
-
_0x50089f();
-
return _0x1b9363;
-
}
最后,新开一个标签页,将代码复制进去,然后缺啥补啥,让这个函数得以运行起来,注意,_0x50089f 这个函数没有什么用,可以直接删除,我得到的结果是这样的:
这样,这三行代码得以解决:
-
var _0x2f2a79;
-
var _0x2cecf5 = _0x288ca0();
-
var _0x3dacec = new _0x2ca1a3["Array"](_0x2cecf5["length"]);
直接在浏览器上运行即可,再看下面的这个for循环:
-
for (var _0x59635d = 0x0; _0x59635d < _0x2cecf5["length"]; _0x59635d++) {
-
_0x3dacec[_0x59635d] = _0x1efa58(_0x62c6cd + _0x2cecf5[_0x59635d]);
-
}
运行报错,
_0x1efa58 is not defined
这是一个函数,直接复制到控制台运行:
-
function _0x1efa58(_0x94e2f4) {
-
var _0x22b242 = 0x0;
-
for (var _0x1856e7 = 0x0; _0x1856e7 < _0x94e2f4["length"]; _0x1856e7++) {
-
_0x22b242 += _0x94e2f4["charCodeAt"](_0x1856e7);
-
}
-
//_0x50089f();
-
return _0x22b242;
-
}
再次运行后,继续报错,
_0x62c6cd is not defined
看代码 function _0x33d434(_0x62c6cd) { 这个原来是参数,我们需要找到它的实参,即看 _0x33d434 这个函数是在哪里调用的:
又追到了这里,实参是 _0x3b88bc(_0x466892),这时我们得计算出它的结果,先看 _0x466892 ,有定义和更新:
-
var _0x466892 = [["navigator", "exists"], ["navigator.vendor", "value"], ["navigator.appName", "value"], ["navigator.plugins.length==0", "value"], ["navigator.platform", "value"], ["navigator.webdriver", "value"], ["platform", "plugin_extentions"], ["ActiveXObject", "exists"], ["webkitURL", "exists"], ["_phantom", "exists"], ["callPhantom", "exists"], ["chrome", "exists"], ["yandex", "exists"], ["opera", "exists"], ["opr", "exists"], ["safari", "exists"], ["awesomium", "exists"], ["puffinDevice", "exists"], ["__nightmare", "exists"], ["domAutomation", "exists"], ["domAutomationController", "exists"], ["_Selenium_IDE_Recorder", "exists"], ["document.__webdriver_script_fn", "exists"], ["document.$cdc_asdjflasutopfhvcZLmcfl_", "exists"], ["process.version", "exists"], ["global.require", "exists"], ["global.process", "exists"], ["WebAssembly", "exists"], ["window.toString()", "value"], ["navigator.cpuClass", "exists"], ["navigator.oscpu", "exists"], ["navigator.connection", "exists"], ["navigator.language=='C'", "value"], ["window.outerWidth==0", "value"], ["window.outerHeight==0", "value"], ["window.WebGLRenderingContext", "exists"], ["window.constructor.toString()", "value"], ["document.documentMode", "value"], ["eval.toString().length", "value"]];
-
_0x466892["push"](["'v4aead2a1deb1b76130ffbb35218fe2b608e5e059756393a0b06378a23b50336a'.toString()", "value"]);
看这个值,都是与浏览器相关的特征,也拷贝到控制台运行,
再将 _0x3b88bc 这个函数 同样拷贝到浏览器运行,然后再赋值给 _0x62c6cd
得到了这么一串与浏览器特征相关的数据,值出来了,再运行这个for循环吧:
-
for (var _0x59635d = 0x0; _0x59635d < _0x2cecf5["length"]; _0x59635d++) {
-
_0x3dacec[_0x59635d] = _0x1efa58(_0x62c6cd + _0x2cecf5[_0x59635d]);
-
}
再次运行,结果如下:
结果出来了,与之前的 值是一样的,我们再来看s的值:
继续将整个值计算出来:
只此,在浏览器上面已正确调试出结果。那如果要在node下面运行成功,要该如何呢?
可以看到,与node唯一的差异只是浏览器特征的检测,也就是这个object:
-
var _0x466892 = [["navigator", "exists"], ["navigator.vendor", "value"], ["navigator.appName", "value"], ["navigator.plugins.length==0", "value"], ["navigator.platform", "value"], ["navigator.webdriver", "value"], ["platform", "plugin_extentions"], ["ActiveXObject", "exists"], ["webkitURL", "exists"], ["_phantom", "exists"], ["callPhantom", "exists"], ["chrome", "exists"], ["yandex", "exists"], ["opera", "exists"], ["opr", "exists"], ["safari", "exists"], ["awesomium", "exists"], ["puffinDevice", "exists"], ["__nightmare", "exists"], ["domAutomation", "exists"], ["domAutomationController", "exists"], ["_Selenium_IDE_Recorder", "exists"], ["document.__webdriver_script_fn", "exists"], ["document.$cdc_asdjflasutopfhvcZLmcfl_", "exists"], ["process.version", "exists"], ["global.require", "exists"], ["global.process", "exists"], ["WebAssembly", "exists"], ["window.toString()", "value"], ["navigator.cpuClass", "exists"], ["navigator.oscpu", "exists"], ["navigator.connection", "exists"], ["navigator.language=='C'", "value"], ["window.outerWidth==0", "value"], ["window.outerHeight==0", "value"], ["window.WebGLRenderingContext", "exists"], ["window.constructor.toString()", "value"], ["document.documentMode", "value"], ["eval.toString().length", "value"]];
-
_0x466892["push"](["'v4aead2a1deb1b76130ffbb35218fe2b608e5e059756393a0b06378a23b50336a'.toString()", "value"]);
以及这个函数检测的结果:
_0x3b88bc(_0x466892)
因此在node环境下面只需要将这些特征补上即可,以
["navigator", "exists"], ["navigator.vendor", "value"]
这两个为例,一个是判断是否存在,一个是计算出它的值,
因此,你需要这样去补:
-
navigator = {};
-
navigator.vendor = "";
补特征是个细心的过程,做的多了,也就很容易了。
文章来源: blog.csdn.net,作者:悦来客栈的老板,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/qq523176585/article/details/110459576
- 点赞
- 收藏
- 关注作者
评论(0)