今天 Jell 发来两个“VIP 视频解析网站”,说想要调试其接口请求过程。博主试了一番,发现其中有反调试和 JS 代码混淆等加密方式。看起来复杂,但实际逆向过程并不难。本文简要记录绕过其反调试的步骤。
避免触及黑产大佬们的利益,以下内容部分主域名用 example 代替。
调试站点A
这是一个名为 “大黑云解析” 的视频解析网站,前缀 https://jx.example.com/?url=,后接原视频地址即可播放,这里以爱奇艺为例:https://www.iqiyi.com/v_19ry0opves.html。
绕过 debugger
打开网址 https://jx.example.com/?url=https://www.iqiyi.com/v_19ry0opves.html 后按 F12 开启调试,Chrome 出现自动断点,可以看到 hykey.js 代码里埋了至少 6 处无限循环的 debugger 指令。

跳过这个 debugger 方法有很多,这里可用到以下三种:
方法一:下载此 js 文件到本地修改断点代码,使用 Fiddler、Charles 或 mitmproxy 等抓包工具将请求映射到本地修改过的 js。
方法二:在 Chrome 开发者工具的 Source 页,点击右上角的 “Deactivate breakpoints” 或使用快捷键 Ctrl + F8 停用所有断点。
方法三:在 debugger 所在行右键下断点选择 “Never pause here” 或 “Add conditional breakpoint” 并添加暂停条件。
这里俺只是想看请求过程, “Deactivate breakpoints” 即可。刷新页面,Network 中找到有返回播放地址的请求如下:
Request URL:https://jx.example.com/dhyjx_ver_9.1.php Request Method:POST Form Data: url: https://www.iqiyi.com/v_19ry0opves.html type: ref: 0 lg: 3 tm: 1591775842 key: 86c792cec5af9952db0c6ded83913524 token: 97ebe413323ce33e2b128a5fd33c600f sdky: 52bb8c8999b34c6c31a73e2eb547c9d9
请求参数有 8 个,其中 key、token 和 sdky 像是 md5 的样子。
全局搜索参数,在首页源码 403 行找到如下代码,可以看出这里面形如 _0x308c('0x1e') 的变量都是混淆过的。
var lg = '3';
var url = 'https://www.iqiyi.com/v_19ry0opves.html';
var type = '';
...
...
if (/Android|webOS|iPhone|iPod|BlackBerry/i[_0x308c('0x10')](navigator[_0x308c('0x1c')])) {
wap = '1';
autoplay = '0';
pic = _0x308c('0x1d');
} else {
wap = '0';
autoplay = '1';
pic = '';
}
key = got(got(url + time() + _0x308c('0x1e')));
var _0x272fa1 = {
'url': url,
'type': type,
'ref': wap,
'lg': lg,
'tm': time(),
'key': key,
'token': token(key),
'sdky': token(time())
};
查找参数来源
先找 key:url 拼接 10 位时间戳再加_0x308c('0x1e') 后传入 got 函数运算 2 次。而传入_0x308c 的参数是常量 0x1e 即 30。
继续向上找_0x308c 的定义处。
_0x308c 函数
此时启用断点调试,停在 debugger 代码处时使用上面方法三的 “Never pause here” 跳过。另外发现跳过 debugger 后继续单步执行时会有个随机的停顿,估计 hykey.js 中有相关延时代码。
找到_0x308c 函数定义:
var _0x308c = function (_0x196cac, _0x332163) {
_0x196cac = _0x196cac - 0x0;
var _0x1efe8e = _0x332d[_0x196cac];
if (_0x308c['RJFFbn'] === undefined) {
(function () {
var _0x438f16 = function () {
var _0x10cfdc;
try {
_0x10cfdc = Function('return\x20(function()\x20' + '{}.constructor(\x22return\x20this\x22)(\x20)' + ');')();
} catch (_0xf31c51) {
_0x10cfdc = window;
}
return _0x10cfdc;
};
var _0x5c3299 = _0x438f16();
var _0x38cf41 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
_0x5c3299['atob'] || (_0x5c3299['atob'] = function (_0x4be497) {
var _0x4dd6c4 = String(_0x4be497)['replace'](/=+$/, '');
for (var _0x1b4982 = 0x0, _0x56afe1, _0x1ca395, _0x47d94a = 0x0, _0x537a12 = ''; _0x1ca395 = _0x4dd6c4['charAt'](_0x47d94a++); ~_0x1ca395 && (_0x56afe1 = _0x1b4982 % 0x4 ? _0x56afe1 * 0x40 + _0x1ca395 : _0x1ca395,
_0x1b4982++ % 0x4) ? _0x537a12 += String['fromCharCode'](0xff & _0x56afe1 >> (-0x2 * _0x1b4982 & 0x6)) : 0x0) {
_0x1ca395 = _0x38cf41['indexOf'](_0x1ca395);
}
return _0x537a12;
});
}
());
_0x308c['utQOsE'] = function (_0x885fea) {
var _0x25046e = atob(_0x885fea);
var _0x1f0347 = [];
for (var _0x596e99 = 0x0, _0x26b49a = _0x25046e['length']; _0x596e99 < _0x26b49a; _0x596e99++) {
_0x1f0347 += '%' + ('00' + _0x25046e['charCodeAt'](_0x596e99)['toString'](0x10))['slice'](-0x2);
}
return decodeURIComponent(_0x1f0347);
};
_0x308c['pOsVIy'] = {};
_0x308c['RJFFbn'] = !![];
}
var _0x537a33 = _0x308c['pOsVIy'][_0x196cac];
if (_0x537a33 === undefined) {
var _0x1ec5f8 = function (_0x11c60b) {
this['rKFhua'] = _0x11c60b;
this['SnPPEL'] = [0x1, 0x0, 0x0];
this['UfgNMw'] = function () {
return 'newState';
};
this['DCkjOO'] = '\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*';
this['ogjJHi'] = '[\x27|\x22].+[\x27|\x22];?\x20*}';
};
_0x1ec5f8['prototype']['fLIrMF'] = function () {
var _0x2a2364 = new RegExp(this['DCkjOO'] + this['ogjJHi']);
var _0x514ce4 = _0x2a2364['test'](this['UfgNMw']['toString']()) ? --this['SnPPEL'][0x1] : --this['SnPPEL'][0x0];
return this['lXpEZI'](_0x514ce4);
};
_0x1ec5f8['prototype']['lXpEZI'] = function (_0x4dc0d2) {
if (!Boolean(~_0x4dc0d2)) {
return _0x4dc0d2;
}
return this['uAtVuP'](this['rKFhua']);
};
_0x1ec5f8['prototype']['uAtVuP'] = function (_0xb65b87) {
for (var _0x106af4 = 0x0, _0x3c4993 = this['SnPPEL']['length']; _0x106af4 < _0x3c4993; _0x106af4++) {
this['SnPPEL']['push'](Math['round'](Math['random']()));
_0x3c4993 = this['SnPPEL']['length'];
}
return _0xb65b87(this['SnPPEL'][0x0]);
};
new _0x1ec5f8(_0x308c)['fLIrMF']();
_0x1efe8e = _0x308c['utQOsE'](_0x1efe8e);
_0x308c['pOsVIy'][_0x196cac] = _0x1efe8e;
} else {
_0x1efe8e = _0x537a33;
}
return _0x1efe8e;
};
这又是一处显而易见的混淆,观察单步调试:
其中 var _0x1efe8e = _0x332d[_0x196cac],_0x196cac 即传入的参数。_0x332d 是前面定义的一个常量数组:var _0x332d = ['YXBwbHk=', 'cmV0dXJuIChmdW5jdGlvbigpIA=='.......],里面的 149 个元素都是 base64 编码。即 var _0x1efe8e 通过索引赋值。而后的函数_0x438f16 及其下方的运算为逐一取数组元素并进行 base64 解码。return _0x1efe8e; 最后返回处理过的变量_0x1efe8e。确定我们要的_0x308c 函数功能为:传入数组索引,返回 base64_decode 后的值。
这里_0x308c('0x1e') 即数组中第 30 个元素 "ZGFoZWl5dW5qaWV4aQ==",解码后为字符串 daheiyunjiexi。
got 函数
上步已把 got 函数的参数确定:url + time() + "daheiyunjiexi",为方便后面验证,这里 url 和 time 分别固定为:https://www.iqiyi.com/v_19ry0opves.html 和 1591775842。拼接后:https://www.iqiyi.com/v_19ry0opves.html1591775842daheiyunjiexi
继续单步执行,在 ACCot.js 中找到 got 函数的定义:
got = function (_0x804b43) {
var _0x4725a4 = {
'GqxOG': _0x2041('0x51'),
'NCWTL': function (_0x3ee3ec, _0x175ccf) {
return _0x3ee3ec > _0x175ccf;
},
'oEHAN': _0x2041('0x52'),
'NnkrC': function (_0xb2d774, _0xc3563a) {
return _0xb2d774(_0xc3563a);
},
'zbpNn': function (_0x2d6e32, _0x1bbb63) {
return _0x2d6e32 + _0x1bbb63;
},
'WyKVR': function (_0x5a2df5, _0x313f5f) {
return _0x5a2df5(_0x313f5f);
},
'lSLin': function (_0x18ebbc, _0x2d3aba) {
return _0x18ebbc + _0x2d3aba;
},
'CnbyC': _0x2041('0x53'),
'dYlfi': _0x2041('0x54')
};
var _0xc5ddb9 = _0x4725a4[_0x2041('0x55')][_0x2041('0x17')]('|'),
_0x5c900a = 0x0;
while (!![]) {
switch (_0xc5ddb9[_0x5c900a++]) {
case '0':
if (_0x4725a4[_0x2041('0x56')](_0x12be4d[_0x2041('0x24')](_0x4725a4[_0x2041('0x57')]), -0x1)) {
var _0x43627b = _0x4725a4[_0x2041('0x58')](md5, _0x4725a4[_0x2041('0x59')](_0x804b43, _0x4f7ba3));
} else {
var _0x43627b = _0x4725a4[_0x2041('0x5a')](md5, _0x4725a4[_0x2041('0x5b')](_0x804b43, _0x14f14f));
}
continue;
case '1':
var _0x12be4d = window[_0x2041('0x5c')][_0x2041('0x5d')];
continue;
case '2':
var _0x14f14f = _0x4725a4[_0x2041('0x5e')];
continue;
case '3':
var _0x4f7ba3 = _0x4725a4[_0x2041('0x5f')];
continue;
case '4':
return _0x43627b;
}
break;
}
};
一眼望去这里面大部分都是混淆过变量名的常量,主要来自函数_0x2041 计算后的返回值,而_0x2041 和上一步中的_0x308c 函数一样:取常量数组里的值后再 base64 解码。稍作还原后是这样的:
got = function (x) {
var _0x41f205 = {
'KPmNM': "3|4|2|0|1",
'kYzEP': function (a, b) {
return a > b;
},
'JsbHA': "jiexi.example.com",
'GapXt': function (a, b) {
return a(b);
},
'UWTDP': function (a, b) {
return a + b;
},
'fPBUv': "daheiyun1888",
'TaMGe': "221"
};
var _0x4816dd = "3|4|2|0|1".split('|'),
_0x14fdb6 = 0;
while (!![]) {
switch (_0x4816dd[_0x14fdb6++]) {
case '0':
if (_0x64c9e7.indexOf("jiexi.example.com") > -1) {
var _0x2b9b8d = md5(x + _0x5672a4);
} else {
var _0x2b9b8d = md5(x + _0x2e10c0);
}
continue;
case '1':
return _0x2b9b8d;
case '2':
var _0x64c9e7 = window.location.host;
continue;
case '3':
var _0x5672a4 = "daheiyun1888", ;
continue;
case '4':
var _0x2e10c0 = "221";
continue;
}
break;
}
};
上面 got 函数的功能就很清楚了:将参数拼接上 daheiyun1888 后 md5 加密。即 key = md5(md5("https://www.iqiyi.com/v_19ry0opves.html1591775842daheiyunjiexi" + "daheiyun1888") + "daheiyun1888"),计算结果 86c792cec5af9952db0c6ded83913524 与第一步中记录的请求参数 key: 86c792cec5af9952db0c6ded83913524 相同。
token 函数
'token': token(key) 和'sdky': token(time()),分别来自 token 函数计算 key 和时间戳的返回值。查找 token 函数,同样在 ACCot.js 中,got 函数下方:
token = function (_0x2c7c73) {
var _0x149f95 = {
'bmsjN': _0x5432('0x63'),
'yQdBl': _0x5432('0x64'),
'usKCQ': _0x5432('0x65'),
'BTGEz': function (_0x2993c1, _0x3ee17e) {
return _0x2993c1 > _0x3ee17e;
},
'RgGSb': _0x5432('0x57'),
'RGEXK': function (_0x4eb698, _0x52d37f) {
return _0x4eb698(_0x52d37f);
},
'runaW': function (_0x564d6d, _0x41fba0) {
return _0x564d6d + _0x41fba0;
},
'Biyzz': function (_0x4712cf, _0x5668c2) {
return _0x4712cf(_0x5668c2);
}
};
var _0x28248e = _0x149f95[_0x5432('0x66')][_0x5432('0x19')]('|'),
_0x1cc8a3 = 0x0;
while (!![]) {
switch (_0x28248e[_0x1cc8a3++]) {
case '0':
return _0x16a3f3;
case '1':
var _0x52ddf8 = window[_0x5432('0x5f')][_0x5432('0x60')];
continue;
case '2':
var _0x3c8092 = _0x149f95[_0x5432('0x67')];
continue;
case '3':
var _0x35b312 = _0x149f95[_0x5432('0x68')];
continue;
case '4':
if (_0x149f95[_0x5432('0x69')](_0x52ddf8[_0x5432('0x26')](_0x149f95[_0x5432('0x6a')]), -0x1)) {
var _0x16a3f3 = _0x149f95[_0x5432('0x6b')](md5, _0x149f95[_0x5432('0x6c')](_0x2c7c73, _0x3c8092));
} else {
var _0x16a3f3 = _0x149f95[_0x5432('0x6d')](md5, _0x149f95[_0x5432('0x6c')](_0x2c7c73, _0x35b312));
}
continue;
}
break;
}
};
和 got 函数的路数一模一样,不再赘叙。还原后:
token = function (x) {
var _0x149f95 = {
'bmsjN': "2|3|1|4|0",
'yQdBl': "daheiyuntoken",
'usKCQ': "shibai",
'BTGEz': function (a, b) {
return a > b;
},
'RgGSb': "jiexi.example.com",
'RGEXK': function (a, b) {
return a(b);
},
'runaW': function (a, b) {
return a + b;
},
'Biyzz': function (a, b) {
return a(b);
}
};
var _0x28248e = "2|3|1|4|0".split('|'),
_0x1cc8a3 = 0;
while (!![]) {
switch (_0x28248e[_0x1cc8a3++]) {
case '0':
return _0x16a3f3;
case '1':
var _0x52ddf8 = window.location.host;
continue;
case '2':
var _0x3c8092 = "daheiyuntoken";
continue;
case '3':
var _0x35b312 = "shibai";
continue;
case '4':
if (_0x52ddf8.indexOf(jiexi.example.com) > -1) {
var _0x16a3f3 = md5(x + "daheiyuntoken");
} else {
var _0x16a3f3 = md5(x + "shibai");
}
continue;
}
break;
}
};
稍有区别的是 md5 参数后拼接字符串为 daheiyuntoken,来验证下对不对:
token = md5("86c792cec5af9952db0c6ded83913524" + "daheiyuntoken") = 97ebe413323ce33e2b128a5fd33c600f
sdky = md5("1591775842" + "daheiyuntoken") = 52bb8c8999b34c6c31a73e2eb547c9d9
与 POST 请求中的参数完全一致。
模拟请求
经测试,模拟请求 Headers 需带上 cookie 和 content-type。Python 实现时,requests.session() 请求首页后在会话中保持 cookie 即可。
调试站点B
站点 B 除了有 debugger 函数,还在页面里加了比较暴力的反调试代码。
pushState 反调试
地址:https://www.example.vip/jiexi/?url= ,添加要解析的地址打开,此时开启 Chrome 调试工具,很快就会出现浏览器卡死的情况。查看其网页源码找到 js 代码:
eval(function (d, f, a, c, b, e) {
b = function (a) {
return a.toString(f)
};
if (!"".replace(/^/, String)) {
for (; a--; )
e[b(a)] = c[a] || b(a);
c = [function (a) {
return e[a]
}
];
b = function () {
return "\\w+"
};
a = 1
}
for (; a--; )
c[a] && (d = d.replace(new RegExp("\\b" + b(a) + "\\b", "g"), c[a]));
return d
}("1 2=c.3('8');4.b(2,'5',{6:7(){1 a=\"\";9(1 i=0;i<d;i++){a=a+i.e();f.g(0,0,a)}}});h.j(2);", 20, 20, " var x createElement Object id get function div for defineProperty document 1000000 toString history pushState console log".split(" "), 0, {}));
又是用了 eval 混淆,console.log 输出看是什么样子:
var x = document.createElement('div');
Object.defineProperty(x, 'id', {
get: function () {
var a = "";
for (var i = 0; i < 1000000; i++) {
a = a + i.toString();
history.pushState(0, 0, a)
}
}
});
console.log(x);
这段代码有两个功能:先用 Object.defineProperty 方法检测浏览器是否打开调试工具;如果打开,则用 history.pushState 函数循环往 history 中写入对象,导致内存占用飙升,浏览器出现假死。
如何解决
最直接的方法是输入网址后,先不要打开控制台。在地址栏输入:javascript:history.pushState = function(){} 覆盖掉网页中的 history.pushState,这样再打开调试工具就不会卡死了。注意:直接复制这段代码到 Chrome 会自动去掉开头的 javascript:,需手动添加才能执行。
当然也可在 Fiddler 或 Charles 中替换成删除相关 js 代码的文件。
请求参数
找到的请求地址和参数如下:
Request URL: https://ckplayer.example.cn/jiexi/api.php Request Method: POST Form Data: url: https://www.iqiyi.com/v_19ry0opves.html referer: aHR0cHM6Ly93d3cuY2twbGF5ZXIudmlwL2ppZXhpLz91cmw9aHR0cHM6Ly93d3cuaXFpeWkuY29tL3ZfMTlyeTBvcHZlcy5odG1s ref: 0 time: 1591785431 type: other: aHR0cHM6Ly93d3cuaXFpeWkuY29tL3ZfMTlyeTBvcHZlcy5odG1s ios:
搜索到参数生成处:
ver = 'VjEuMS4yLjIwMTkwNTIw';
auto = '1';
auto_h5 = '1';
var str_href = window.location.href, other_l = str_href.substring(str_href.indexOf('=') + 1);
if (!other_l) {
var other_l = 'kkkk';
};
function player() {
var y = new Base64();
var isiPad = navigator.userAgent.match(/iPad|iPhone|Android|Linux|iPod/i) != null;
var form;
if (isiPad) {
form = '1';
} else {
form = '0';
}
$.post("api.php", {
'url': 'https://www.iqiyi.com/v_19ry0opves.html',
'referer': 'aHR0cHM6Ly93d3cuY2twbGF5ZXIudmlwL2ppZXhpLz91cmw9aHR0cHM6Ly93d3cuaXFpeWkuY29tL3ZfMTlyeTBvcHZlcy5odG1s',
'ref': form,
'time': '1591785431',
'type': '',
'other': y.encode(other_l),
'ref': form,
'ios': ''
}
这里的参数比站点A 来的更简单,其中 referer 为当前页面完整 URL 再 base64 编码,可以为固定值。other:y.encode(other_l) 里的 y 是 Base64(),即将 other_l 进行 base64 编码,而 other_l 参数来自前面定义:截取当前打开页面的 url,即我们提交的播放地址。尝试 base64 解码 aHR0cHM6Ly93d3cuaXFpeWkuY29tL3ZfMTlyeTBvcHZlcy5odG1s 也可以验证这点。
后记
疫情期间大家都蹲在家里看剧,线上视频流量暴增,加上前段时间的 “超前点播” 风波,导致一部分网友又转回盗版视频网站,盗版片源的市场需求又大涨了。网上类似这种 “VIP 视频解析网站” 都打着 “一键解析爱奇艺、优酷、腾讯视频等各大网站的 VIP 电影电视剧集,且跳过广告” 的旗号,实在 “良心”。
细心的老哥们可能已经发现,这些解析后获得的 VIP 视频播放地址,很多都不是来自官方网站,而是私人服务器。
流媒体服务器价格不菲,而且接口提供者无利不起早,通常的盈利模式就是弹广告了。这些网站通过视频解析绕过了 VIP 收费机制,接入广告分发平台,并在视频中植入色情、博彩的推广广告,早在很多年前就已形成了成熟的产业链。
以前有比较粗暴的,直接在播放页悬挂各种大幅的色情、博彩广告;有些会在视频源中插入 “澳门赌场” 字幕等。现在随着主要流量转移到了移动端,加上国内监管越来越严,黑产大佬们也各显神通,广告代码的植入越来越隐蔽。博主调试时也发现了几段广告的展示代码,记录在下方。
广告代码
为逃避监管,广告链接通常会多次跳转;弹窗也会随机、按平台展示。
1、比如下面这个第一层跳转,先通过 userAgent 判断展示方式,刻意规避了 UA 为 MicroMessenger 的微信端使用 iframe 展示:
(function () {
if ((/(MQQBrowser|UCBrowser|baidu)/i.test(navigator.userAgent)) && !(/(MicroMessenger)/i.test(navigator.userAgent))) {
window.addEventListener('message', function (e) {
if (e.data.web_id == '4817') {
eval(e.data.code)
}
});
var body = document.getElementsByTagName('body')[0];
var iframe = document.createElement('iframe');
iframe.src = 'https://xg.example.cn/c/4817?is_iframe=1&time=' + Math.random();
iframe.style = 'display: none;';
body.appendChild(iframe)
} else {
var body = document.getElementsByTagName('body')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://xg.example.cn/c/4817?time=' + Math.random();
body.appendChild(script)
}
})();
2、第二层跳转时,先判断 UA 为手机浏览器时才继续执行相关 js,否则返回 console.log(' 禁止访问的系统。')。模拟手机浏览器打开后又是茫茫多的 js 代码,很直观的可以看到是个广告 SDK,其中代码功能包括收集操作系统、手机型号、浏览器种类、Canvas 指纹、屏幕尺寸等,此外存储了各种加密的广告参数,包括推送地址、存储目录、广告联盟代码等。
lh.Data = {
size: parseInt("1"),
matter: JSON.parse('[{"path":"2020\/05\/20200929670.gif","size":81,"width":640,"height":200},{"path":"2020\/05\/20200920671.gif","size":97,"width":640,"height":200}]'),
purl: "https://xc.example.cn",
murl: "https://image.example.cn",
string: "YWR2ZXJ0aXNlcl9hZF9pZD0yNDI5JmFkdmVydGlzZXJfaWQ9MTQ4MiZjb21wZWxfc2tpcD0xMCZmaXJzdF9jb21wZWxfc2tpcD0xNiZoaWRfY2hhbmNlPTgwJmlzX3dlY2hhdF9jb3Zlcj0wJmlzX3dlY2hhdF9vdXRfc2tpcD0xJnBvc2l0aW9uX2lkPTExJnJldHVybl9jaGFuY2U9MCZ0aW1lPTE1OTE3OTQwODcmdHlwZT0xJndlYm1hc3Rlcl9hZF9pZD00ODE3JndlYm1hc3Rlcl9pZD0zODcyJmtleT0yOGE0MWZlY2NlMzVjOTJlZmNkNTc2ZTY4YTg1Zjk1MQ==",
rskip_str: "YWRpZHM9MjQzNSwyMzM2LDI0MTcsMTUxNywyNDI5JnRpbWU9MTU5MTc5NDA4NyZrZXk9NDViZTczNDMzOWQ0NGJlNTUxNDNjNDBiYThmZjM3YzM=",
is_check: parseInt("0"),
com_cli: "1",
com_cha: parseInt("30"),
com_cli_inter: parseInt("5"),
false_clo: parseInt("80"),
hid_height: parseInt("270"),
is_wechat: "",
position: parseInt("2"),
style: "",
style_type: parseInt("1"),
link: "https://mob18.example.com/42a2506082e785d99b5c3c9a92783879/",
js_effects: "1",
adid: "4817",
click_return: JSON.parse('{"state":1,"chance":"10"}'),
own_return: JSON.parse('{"state":0,"chance":100,"number":0}'),
other_return: JSON.parse('{"state":0,"chance":100,"number":0}'),
is_jiexi: parseInt("0"),
statis_code: "",
statis_code_ratio: parseInt("100"),
advid: "2429",
ip2long: "976257987",
dis_time: parseInt("10769"),
index: "2147483647",
};
经过博主多次测试,正如上面提到的展示广告多为:博彩、色情网站、色情直播平台推广等违法广告。
参考资料
- 经验分享 | JavaScript 反调试技巧
- 突破前端反调试 -- 阻止页面不断 debugger
- 破解前端定时 debugger 反调试
- JS 反调试技术随笔记录
- 如何解决反调试困扰 - chrome extension
- Anti Anti-debugger
- 前端开发中如何在 JS 文件中检测用户浏览器是否打开了调试面板
- 深夜话题:这十二行代码是如何让浏览器爆炸的?
- 让浏览器爆炸的 JAVASCRIPT:HTML5 API 之 HISTORY.PUSHSTATE
- 【单页应用巨坑之 History】细数 History 带给单页应用的噩梦
- 记一次 anti anti debug
- 浏览器地址栏运行 JavaScript 代码
- 东南亚博彩行业浮世绘,道尽黑产从业百态
- [调试逆向] 简书网页劫持分析,使用 Chrome DevTools 调试 JavaScript 技巧,利用 CSP 预防劫持