声明:案例分析仅供学习交流使用,勿用于任何非法用途。如学习者进一步逆向并对版权方造成损失,请自行承担法律后果,本人概不负责。
该页面用于调试的F12,鼠标右键,CTRL + S都被禁用了。
从别的页面打开控制台过来,会遇到两道防御机制:
1.页面被改为无效内容。
2.控制台无限打开VMXX页面卡调试。
摸排源码发现两个可疑函数 endebug() 与 txsdefwsw() :
endebug()函数会在调试模式下篡改页面内容。
function endebug(off, code) {
if (!off) {
!function(e) {
function n(e) {
function n() {
return u
}
function o() {
window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized ? t("on") : (a = "off",
console.log(d),
console.clear(),
t(a))
}
function t(e) {
u !== e && (u = e,
"function" == typeof c.onchange && c.onchange(e))
}
function r() {
l || (l = !0,
window.removeEventListener("resize", o),
clearInterval(f))
}
"function" == typeof e && (e = {
onchange: e
});
var i = (e = e || {}).delay || 500
, c = {};
c.onchange = e.onchange;
var a, d = new Image;
d.__defineGetter__("id", function() {
a = "on"
});
var u = "unknown";
c.getStatus = n;
var f = setInterval(o, i);
window.addEventListener("resize", o);
var l;
return c.free = r,
c
}
var o = o || {};
o.create = n,
"function" == typeof define ? (define.amd || define.cmd) && define(function() {
return o
}) : "undefined" != typeof module && module.exports ? module.exports = o : window.jdetects = o
}(),
jdetects.create(function(e) {
var a = 0;
var n = setInterval(function() {
if ("on" == e) {
setTimeout(function() {
if (a == 0) {
a = 1;
setTimeout(code)
}
}, 200)
}
}, 100)
})
}
}
txsdefwsw()函数将混淆的“debugger”指令还原后放入无限递归的函数中执行,导致控制台不停打开VMXX并自动断点。虽然可以用值为false的条件断点跳过,但不停执行这段代码也会导致卡顿。
function txsdefwsw() {
var r = "V"
, n = "5"
, e = "8";
function o(r) {
if (!r)
return "";
for (var t = "", n = 44106, e = 0; e < r.length; e++) {
var o = r.charCodeAt(e) ^ n;
n = n * e % 256 + 2333,
t += String.fromCharCode(o)
}
return t
}
try {
var a = [
"r",
o("갯"), //e
"g",
o("갭"), //g
function(t) { //u
if (!t)
return "";
for (var o = "", a = r + n + e + "7", c = 45860, f = 0; f < t.length; f++) {
var i = t.charCodeAt(f);
c = (c + 1) % a.length,
i ^= a.charCodeAt(c),
o += String.fromCharCode(i)
}
return o
}("@"),
"b",
"e",
"d"
].reverse().join(""); //debugger
!function c(r) {
(1 !== ("" + r / r).length || 0 === r) && function() {} .constructor(a)(), //等价于 !function(){a}()
c(++r) //无限递归
}(0)
} catch (a) {
setTimeout(txsdefwsw, 100)
}
}
那么,我们只要在函数处下断,待其加载后将其制空是不是就可以了呢。
结果还是没有有效内容。
又经过一段排查,居然发现其第三方库jquery.min.js中居然被做了手脚。
这两个函数初看上去叫人摸不着头脑,但仔细看最后的字符串不难发现端倪——这两就是混淆后的endebug()与txsdefwsw()代码。也就是说该网页在jquery.min.js中用eval()函数再次激活自己的反调试机制,并且由于代码的混淆存储,这段很难被定位和静态分析。
eval(function (p, a, c, k, e, d) {
e = function (c) {
return (c < a ? "" : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
};
if (!''.replace(/^/, String)) {
while (c--) d[e(c)] = k[c] || e(c);
k = [
function (e) {
return d[e]
}
];
e = function () {
return '\\w+'
};
c = 1;
};
while (c--)
if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]);
return p;
}('2 M(g,y){h(!g){!2(e){2 n(e){2 n(){j u}2 o(){4.8&&4.8.m&&4.8.m.H?t("9"):(a="g",v.G(d),v.F(),t(a))}2 t(e){u!==e&&(u=e,"2"==7 c.5&&c.5(e))}2 r(){l||(l=!0,4.I("k",o),L(f))}"2"==7 e&&(e={5:e});3 i=(e=e||{}).K||J,c={};c.5=e.5;3 a,d=B z;d.D("C",2(){a="9"});3 u="E";c.A=n;3 f=s(o,i);4.S("k",o);3 l;j c.N=r,c}3 o=o||{};o.x=n,"2"==7 6?(6.P||6.R)&&6(2(){j o}):"O"!=7 b&&b.p?b.p=o:4.q=o}(),q.x(2(e){3 a=0;3 n=s(2(){h("9"==e){w(2(){h(a==0){a=1;w(y)}},Q)}},T)})}}', 56, 56, '||function|var|window|onchange|define|typeof|Firebug|on||module|||||off|if||return|resize||chrome|||exports|jdetects||setInterval|||console|setTimeout|create|code|Image|getStatus|new|id|__defineGetter__|unknown|clear|log|isInitialized|removeEventListener|500|delay|clearInterval|endebug|free|undefined|amd|200|cmd|addEventListener|100'.split('|'), 0, {}));
eval(function (p, a, c, k, e, d) {
e = function (c) {
return (c < a ? "" : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
};
if (!''.replace(/^/, String)) {
while (c--) d[e(c)] = k[c] || e(c);
k = [
function (e) {
return d[e]
}
];
e = function () {
return '\\w+'
};
c = 1;
};
while (c--)
if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]);
return p;
}('3 l(){2 r="v",n="5",e="8";3 o(r){m(!r)4"";h(2 t="",n=w,e=0;e<r.6;e++){2 o=r.9(e)^n;n=n*e%u+p,t+=k.j(o)}4 t}q{2 a=["r",o("z"),"g",o("x"),3(t){m(!t)4"";h(2 o="",a=r+n+e+"7",c=y,f=0;f<t.6;f++){2 i=t.9(f);c=(c+1)%a.6,i^=a.9(c),o+=k.j(i)}4 o}("@"),"b","e","d"].B().E("");!3 c(r){(1!==(""+r/r).6||0===r)&&3(){}.D(a)(),c(++r)}(0)}C(a){A(l,s)}}', 41, 41, '||var|function|return||length|||charCodeAt||||||||for||fromCharCode|String|txsdefwsw|if|||2333|try||100||256|V|44106|갭|45860|갯|setTimeout|reverse|catch|constructor|join'.split('|'), 0, {}));
把jquery.min.js拿下来,注释掉反调试部分,替换掉原响应。
但是替换掉之后,还是会被反调试挡住,明明被置空的函数又“复活”了。
再次调试,发现其子页面还有相同的代码执行,注意最开始的反调试函数在 (index) 中,现在是 /city_realtime.php?v=2.3 中,其实左侧的每个子Tab页都有这段反调试。
只要再次置空这两个函数,就能正常打开调试模式了。