satan 通过 Google 阅读器发送给您的内容:
最近在用某商业软件时遇到了一个加密js , 稍微破解了一下, 在这里记录破解过程.
分析原文件
这是某商业软件的一个js 文件, 为了不透露软件信息和节约篇幅, 在此将长度为120KB 的n0 字符串缩短.
(function () { var V0 = 0, e0 = 0, f0 = '~', L0 = "", R0 = new Array(119887, 17, 68, 64, 9, 6, 63, 16, 50, 29, 40, 45, 69, 10, 38, 20, 34, 41, 30, 2, 31, 28, 39, 74, 15, 37, 23, 51, 8, 11, 33, 12, 65, 1, 25, 58, 7, 53, 70, 72, 21, 67, 43, 54, 55, 35, 26, 19, 27, 62, 22, 71, 47, 32, 18, 49, 36, 60, 24, 57, 48, 13, 44, 66, 14, 42, 4, 46, 5, 56, 73, 61, 59, 52, 3), k0 = arguments.callee.toString().replace(/[\s\'\"\)\}\]\[\;\.\{\(]/g, "").length; function r0(W0, M0) { return W0 - M0 } var n0 = "`sftldour/b`mmdd/unRushof)(/sdqm`bd).Z]r]&]#](]|]\\]Z]:]/]z])\\.f-##(/mdofuiw`s!...dmrd!vhoenv/qshou)(:|:"; var X0 = R0.sort(r0); var Q0 = X0[R0.length - 1]; while (V0 < R0.length - 1) { L0 = L0 + String.fromCharCode(n0.charCodeAt(X0[V0] - (k0 - Q0)) ^ 1); V0++ } var x0 = eval(L0), w0 = ""; for (var a0 = 0; a0 < n0.length; a0 += x0 - Q0) { if (a0 == X0[e0] - 1 && e0 < R0.length - 1) { e0++ } else { if (n0.charAt(a0) == f0) { w0 = w0 + f0 } else { w0 = w0 + String.fromCharCode(n0.charCodeAt(a0) ^ 1) } } } eval(w0) })();
可以看到, 在eval(w0)
之前的部分只有一个目的: 生成可执行的js 语句, 存放在w0
字符串中, 供eval()
执行. 只要我们拿到了w0
的内容, 这个js 文件就告破了.
有心急的同学说, 我们修改eval(w0)
为console.log(w0)
, 不就可以在控制台里读到w0
的内容了吗? 想法很好很直接, 可惜加密这个文件的人也知道一个eval()
还不够, 他为这个函数布置了两重保护:
k0=arguments.callee.toString().replace(/[\s\'\"\)\}\]\[\;\.\{\(]/g,"").length
中的arguments.callee
是对整个匿名函数的引用,k0
是一个很重要的值, 随意修改函数体可能造成k0
改变而影响功能.var x0=eval(L0)
中的L0
也很关键. 实际上, 我们尝试一下定长修改函数体, 即便k0
的值没有变, 但L0
却变了, 造成函数不能运行, 这个有点像我们常见的签名或hash 算法.
也就是说, 这个匿名函数的函数体绝对不能动, 我们要破解它, 只能先在不修改函数体的前提下分别计算出k0 和L0 , 从而解除这两重保护.
破解第一重保护
针对第一重保护, 我们先算k0
. 把代码稍作变动, 为这个匿名函数起一个变量名func
, 然后计算k0
:
var func=function () { var V0 = 0, e0 = 0, f0 = '~', L0 = "", R0 = new Array(119887, 17, 68, 64, 9, 6, 63, 16, 50, 29, 40, 45, 69, 10, 38, 20, 34, 41, 30, 2, 31, 28, 39, 74, 15, 37, 23, 51, 8, 11, 33, 12, 65, 1, 25, 58, 7, 53, 70, 72, 21, 67, 43, 54, 55, 35, 26, 19, 27, 62, 22, 71, 47, 32, 18, 49, 36, 60, 24, 57, 48, 13, 44, 66, 14, 42, 4, 46, 5, 56, 73, 61, 59, 52, 3), k0 = arguments.callee.toString().replace(/[\s\'\"\)\}\]\[\;\.\{\(]/g, "").length; function r0(W0, M0) { return W0 - M0 } var n0 = "`sftldour/b`mmdd/unRushof)(/sdqm`bd).Z]r]&]#](]|]\\]Z]:]/]z])\\.f-##(/mdofuiw`s!...dmrd!vhoenv/qshou)(:|:"; var X0 = R0.sort(r0); var Q0 = X0[R0.length - 1]; while (V0 < R0.length - 1) { L0 = L0 + String.fromCharCode(n0.charCodeAt(X0[V0] - (k0 - Q0)) ^ 1); V0++ } var x0 = eval(L0), w0 = ""; for (var a0 = 0; a0 < n0.length; a0 += x0 - Q0) { if (a0 == X0[e0] - 1 && e0 < R0.length - 1) { e0++ } else { if (n0.charAt(a0) == f0) { w0 = w0 + f0 } else { w0 = w0 + String.fromCharCode(n0.charCodeAt(a0) ^ 1) } } } eval(w0) }; console.log(func.toString().replace(/[\s\'\"\)\}\]\[\;\.\{\(]/g,"").length);
运行后在控制台里看到结果是119888, 这样k0 我们就算出来了, 第一重保护告破.
现在我们把k0=arguments.callee.toString().replace(/[\s\'\"\)\}\]\[\;\.\{\(]/g,"").length
改成k0=119888;
, 运行func()
, 却遇到了报错. 你看我又忘记了, 现在还没解除第二重保护, 还不能直接修改函数体.
破解第二重保护
那么我们怎样才能在不动函数体的情况下计算出L0
呢? 想一想, 里面的局部变量全部不能动, 只能从全局函数eval
入手了. 我们在函数体之外把eval
替换掉:
var eval=console.log; var func=function () { var V0 = 0, e0 = 0, f0 = '~', L0 = "", R0 = new Array(119887, 17, 68, 64, 9, 6, 63, 16, 50, 29, 40, 45, 69, 10, 38, 20, 34, 41, 30, 2, 31, 28, 39, 74, 15, 37, 23, 51, 8, 11, 33, 12, 65, 1, 25, 58, 7, 53, 70, 72, 21, 67, 43, 54, 55, 35, 26, 19, 27, 62, 22, 71, 47, 32, 18, 49, 36, 60, 24, 57, 48, 13, 44, 66, 14, 42, 4, 46, 5, 56, 73, 61, 59, 52, 3), k0 = arguments.callee.toString().replace(/[\s\'\"\)\}\]\[\;\.\{\(]/g, "").length; function r0(W0, M0) { return W0 - M0 } var n0 = "`sftldour/b`mmdd/unRushof)(/sdqm`bd).Z]r]&]#](]|]\\]Z]:]/]z])\\.f-##(/mdofuiw`s!...dmrd!vhoenv/qshou)(:|:"; var X0 = R0.sort(r0); var Q0 = X0[R0.length - 1]; while (V0 < R0.length - 1) { L0 = L0 + String.fromCharCode(n0.charCodeAt(X0[V0] - (k0 - Q0)) ^ 1); V0++ } var x0 = eval(L0), w0 = ""; for (var a0 = 0; a0 < n0.length; a0 += x0 - Q0) { if (a0 == X0[e0] - 1 && e0 < R0.length - 1) { e0++ } else { if (n0.charAt(a0) == f0) { w0 = w0 + f0 } else { w0 = w0 + String.fromCharCode(n0.charCodeAt(a0) ^ 1) } } } eval(w0) }; func();
运行一下, 在执行eval(L0)
时, 在控制台里可以看到, 原来L0
是一个字符串, 其内容为arguments.callee.toString().replace(/[\s\'\"\)\}\]\[\;\.\{\(]/g,"").length
, 这意味着var x0 = eval(L0)
的值也应该是119888. 第二重保护告破.
得到最终结果
好了, 现在我们可以开始动手修改函数体了, 把k0 和x0 的值都改成119888 :
var eval=console.log; var func=function () { var V0 = 0, e0 = 0, f0 = '~', L0 = "", R0 = new Array(119887, 17, 68, 64, 9, 6, 63, 16, 50, 29, 40, 45, 69, 10, 38, 20, 34, 41, 30, 2, 31, 28, 39, 74, 15, 37, 23, 51, 8, 11, 33, 12, 65, 1, 25, 58, 7, 53, 70, 72, 21, 67, 43, 54, 55, 35, 26, 19, 27, 62, 22, 71, 47, 32, 18, 49, 36, 60, 24, 57, 48, 13, 44, 66, 14, 42, 4, 46, 5, 56, 73, 61, 59, 52, 3), k0 = 119888; function r0(W0, M0) { return W0 - M0 } var n0 = "`sftldour/b`mmdd/unRushof)(/sdqm`bd).Z]r]&]#](]|]\\]Z]:]/]z])\\.f-##(/mdofuiw`s!...dmrd!vhoenv/qshou)(:|:"; var X0 = R0.sort(r0); var Q0 = X0[R0.length - 1]; while (V0 < R0.length - 1) { L0 = L0 + String.fromCharCode(n0.charCodeAt(X0[V0] - (k0 - Q0)) ^ 1); V0++ } var x0 = 119888, w0 = ""; for (var a0 = 0; a0 < n0.length; a0 += x0 - Q0) { if (a0 == X0[e0] - 1 && e0 < R0.length - 1) { e0++ } else { if (n0.charAt(a0) == f0) { w0 = w0 + f0 } else { w0 = w0 + String.fromCharCode(n0.charCodeAt(a0) ^ 1) } } } eval(w0) }; func();
运行一下, 控制台里出现了w0
的内容, 也就是我们想要得到的解密后的内容.
在破解这个文件的过程中, 最主要最关键的步骤就是把eval
指向console.log
. 这种修改全局函数的方法应该对各种基于eval
的js 加密都管用, 我决定把这种方法叫做"eval hooking" .
ps. 其实我这是故意带着读者绕弯路: 只要在运行js 的同时打开firebug 的script tab , 就能看到eval()
的结果, 比这篇文章里所说的方法要简单得多.
转载请注明以下信息
文章转载自:鲁夫的爱 [ http://opengg.me/ ]
本文标题:破解一个加密的JS 文件
本文地址:http://opengg.me/787/decrypt-javascript-via-eval-hooking/
您可能也喜欢: | ||||
不Google,毋宁死 – 实时生成的Google hosts文件 | 存个档, 破解优酷和土豆的播放器 | YSSY.Ajax.Uploader - 饮水思源文件批量上传脚本 | YSSY.Ajax.Uploader - 饮水思源文件上传脚本 | 我所见的,IT人士对用户密码安全问题的常见误解 |
无觅 |
可从此处完成的操作:
- 使用 Google 阅读器订阅鲁夫的爱
- 开始使用 Google 阅读器,轻松地与您喜爱的所有网站保持同步更新
没有评论:
发表评论