记载火狐浏览器下的一次新手级的js解密工作
警告:该随笔内容仅用于合法范围下的学习,不得用于任何商业和非法用途,不得未经授权转载,否则后果自负。
首先是需要解密的网站:https://www.aqistudy.cn/historydata/monthdata.php?city=%E5%8C%97%E4%BA%AC,在这个网址尝试按鼠标右键/F12会跳出禁止调试对话框。因此为了进入F12界面,可以右键后再按f12.
然而该网站会检测到f12的打开,并立即改写网页内容。而在f12里面,会频繁跳出debugger,只能查出一段含有["constructor"]("debugger")())的混淆后的SOURCE代码,如下(后面我会我用省略号省去大多数加密后的枯燥的内容)
var debugflag = false;
document.onkeydown = function() {
if ((e.ctrlKey) && (e.keyCode == 83)) {
alert("检测到非法调试,CTRL + S被管理员禁用");
return false;
}
}
document.onkeydown = function() {
var e = window.event || arguments[0];
if (e.keyCode == 123) {
alert("检测到非法调试,F12被管理员禁用");
return false;
}
}
document.oncontextmenu = function() {
alert('检测到非法调试,右键被管理员禁用');
return false;
}
!function () {
if (window.outerWidth - window.innerWidth > 210 ||
window.outerHeight - window.innerHeight > 210) {
$('#body').html('检测到非法调试, 请关闭调试终端后刷新本页面重试!<br/>Welcome for People, Not Welcome for Machine!<br/>');
debugflag = true;
}
const handler = setInterval(() => {
if (window.outerWidth - window.innerWidth > 210 ||
window.outerHeight - window.innerHeight > 210) {
$('#body').html('检测到非法调试, 请关闭调试终端后刷新本页面重试!<br/>Welcome for People, Not Welcome for Machine!<br/>');
debugflag = true;
}
const before = new Date();
(function() {}
["constructor"]("debugger")())
const after = new Date();
const cost = after.getTime() - before.getTime();
if (cost > 50) {
debugflag = true;
document.write('检测到非法调试, 请关闭调试终端后刷新本页面重试!<br/>');
document.write("Welcome for People, Not Welcome for Machine!<br/>");
}
}, 2000)
}();
所以无法得知产生debugger的根本源码在哪儿。
经过一番折腾,最终猜测https://www.aqistudy.cn/historydata/resource/js/deJvi1NlJdQpH.min.js?t=1631459101这个代码就是要找的。代码内容如下:
eval(function(p,a,c,k,e,d){e=function(c){return c};if(!''.replace(/^/,String)){while(c--){d[c]=k[c]||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(0(0(\'1\')))',3,3,'dweklxde|WT省略号XA5|eval'.split('|'),0,{}))
这是一段超长的代码,连我的油猴编辑器都变卡了。现在分析一下简化后的代码(把eval去掉了):
(function ANM(p, a, c, k, e, d) {
e = function(c) {
return c
};
if (!''.replace(/^/, String)) {
while (c--) {
d[c] = k[c] || 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(0(0(\'1=\')))', 3, 3, 'dweklxde|WT省略号Dk|eval'.split('|'), 0, {})
这是一个传入6个形参的匿名函数,定义后就立即执行(传入实参,实例化)。经过在StackOverflow上的检索,我知道了if (!''.replace(/^/, String)) 的作用,大致是判断replace在当前浏览器的兼容性如何的,这不是重点,当下还是解密为重。把原来的密文传入后,运行的结果为:
"eval(dweklxde(dweklxde('WT省略号Dk=')))"
代码中再次出现了eval,此外还出现了两次dweklxde这段申必代码。故我在网站全套源码里检索了这个申必代码,并惊喜的发现了其定义:
function dweklxde(tsdx){
var b=new Base64();
return b.decode(tsdx)
}
把eval去掉后,在原网页控制台执行dweklxde(dweklxde(残余的密文)),得到了以下输出:
const askhMzDQ7qNo = "aTtcZsDM6Q4W30w5";//AESkey,可自定义
const asium9nuSIrA = "bf4STpo4157Vwqwb";//密钥偏移量IV,可自定义
const ackDrdNRViAM = "dbxOAFBzWKAtR7G8";//AESkey,可自定义
const aciyyIkBh3oq = "fNEcReKtHOhYss8e";//密钥偏移量IV,可自定义
const dskcfiXeCyq7 = "h2cTaMqHmG86zGgs";//DESkey,可自定义
const dsiRXgqLSp5I = "xbnl4T59FFDkdlBc";//密钥偏移量IV,可自定义
const dckUcY5kLrAk = "oihJrNyLdihbAQpu";//DESkey,可自定义
const dcihFBYD1foW = "pGOltTq5bcJfOBBY";//密钥偏移量IV,可自定义
const aes_local_key = 'emhlbnFpcGFsbWtleQ==';
const aes_local_iv = 'emhlbnFpcGFsbWl2';
var BASE64 = {
encrypt: function(text) {
var b = new Base64();
return b.encode(text);
},
decrypt: function(text) {
var b = new Base64();
return b.decode(text);
}
};
var DES = {
encrypt: function(text, key, iv){
var secretkey = (CryptoJS.MD5(key).toString()).substr(0, 16);
var secretiv = (CryptoJS.MD5(iv).toString()).substr(24, 8);
secretkey = CryptoJS.enc.Utf8.parse(secretkey);
secretiv = CryptoJS.enc.Utf8.parse(secretiv);
var result = CryptoJS.DES.encrypt(text, secretkey, {
iv: secretiv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return result.toString();
},
decrypt: function(text, key, iv){
var secretkey = (CryptoJS.MD5(key).toString()).substr(0, 16);
var secretiv = (CryptoJS.MD5(iv).toString()).substr(24, 8);
secretkey = CryptoJS.enc.Utf8.parse(secretkey);
secretiv = CryptoJS.enc.Utf8.parse(secretiv);
var result = CryptoJS.DES.decrypt(text, secretkey, {
iv: secretiv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return result.toString(CryptoJS.enc.Utf8);
}
};
var AES = {
encrypt: function(text, key, iv) {
var secretkey = (CryptoJS.MD5(key).toString()).substr(16, 16);
var secretiv = (CryptoJS.MD5(iv).toString()).substr(0, 16);
// console.log('real key:', secretkey);
// console.log('real iv:', secretiv);
secretkey = CryptoJS.enc.Utf8.parse(secretkey);
secretiv = CryptoJS.enc.Utf8.parse(secretiv);
var result = CryptoJS.AES.encrypt(text, secretkey, {
iv: secretiv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return result.toString();
},
decrypt: function(text, key, iv) {
var secretkey = (CryptoJS.MD5(key).toString()).substr(16, 16);
var secretiv = (CryptoJS.MD5(iv).toString()).substr(0, 16);
secretkey = CryptoJS.enc.Utf8.parse(secretkey);
secretiv = CryptoJS.enc.Utf8.parse(secretiv);
var result = CryptoJS.AES.decrypt(text, secretkey, {
iv: secretiv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return result.toString(CryptoJS.enc.Utf8);
}
};
var localStorageUtil = {
save: function(name, value) {
var text = JSON.stringify(value);
text = BASE64.encrypt(text);
text = AES.encrypt(text, aes_local_key, aes_local_iv);
try {
localStorage.setItem(name, text);
} catch (oException) {
if (oException.name === 'QuotaExceededError') {
console.log('Local limit exceeded');
localStorage.clear();
localStorage.setItem(name, text);
}
}
},
check: function(name) {
return localStorage.getItem(name);
},
getValue: function(name) {
var text = localStorage.getItem(name);
var result = null;
if (text) {
text = AES.decrypt(text, aes_local_key, aes_local_iv);
text = BASE64.decrypt(text);
result = JSON.parse(text);
}
return result;
},
remove: function(name) {
localStorage.removeItem(name);
}
};
// console.log('base64', BASE64.encrypt('key'));
function dAel1E5FtZ8(pf5OVah) {
pf5OVah = DES.decrypt(pf5OVah, dskcfiXeCyq7, dsiRXgqLSp5I);
return pf5OVah;
}
function dcLpsq6sB7(pf5OVah) {
pf5OVah = AES.decrypt(pf5OVah, askhMzDQ7qNo, asium9nuSIrA);
return pf5OVah;
}
function gEE7JrZUSW45M88T(key, period) {
if (typeof period === 'undefined') {
period = 0;
}
var d = DES.encrypt(key);
d = BASE64.encrypt(key);
var data = localStorageUtil.getValue(key);
if (data) { // 判断是否过期
const time = data.time;
const current = new Date().getTime();
if (new Date().getHours() >= 0 && new Date().getHours() < 5 && period > 1) {
period = 1;
}
if (current - (period * 60 * 60 * 1000) > time) { // 更新
data = null;
}
// 防止1-5点用户不打开页面,跨天的情况
if (new Date().getHours() >= 5 && new Date(time).getDate() !== new Date().getDate() && period === 24) {
data = null;
}
}
return data;
}
function oshxcxc8Lo(obj) {
var newObject = {};
Object.keys(obj).sort().map(function(key){
newObject[key] = obj[key];
});
return newObject;
}
function dhv7s6TfbwDPIepj215O(data) {
data = BASE64.decrypt(data);
data = DES.decrypt(data, dskcfiXeCyq7, dsiRXgqLSp5I);
data = AES.decrypt(data, askhMzDQ7qNo, asium9nuSIrA);
data = BASE64.decrypt(data);
return data;
}
var pNOZW4pEhNH = (function(){
function oshxcxc8Lo(obj){
var newObject = {};
Object.keys(obj).sort().map(function(key){
newObject[key] = obj[key];
});
return newObject;
}
return function(mOAJWRjCd, oyNl2g){
var aRT5 = '3945282e47e176b3af7c4cf62edf0cf5';
var cXlfM = 'WEB';
var t1PYHUr = new Date().getTime();
var pf5OVah = {
appId: aRT5,
method: mOAJWRjCd,
timestamp: t1PYHUr,
clienttype: cXlfM,
object: oyNl2g,
secret: hex_md5(aRT5 + mOAJWRjCd + t1PYHUr + cXlfM + JSON.stringify(oshxcxc8Lo(oyNl2g)))
};
pf5OVah = BASE64.encrypt(JSON.stringify(pf5OVah));
pf5OVah = DES.encrypt(pf5OVah, dckUcY5kLrAk, dcihFBYD1foW);
return pf5OVah;
};
})();
function spRLIQarqGIFjayN1z(mOAJWRjCd, o8WGj0NwLH, c6yqLt8Q0, pVBXfIU) {
const krya = hex_md5(mOAJWRjCd + JSON.stringify(o8WGj0NwLH));
const dqlC4 = gEE7JrZUSW45M88T(krya, pVBXfIU);
if (!dqlC4) {
var pf5OVah = pNOZW4pEhNH(mOAJWRjCd, o8WGj0NwLH);
$.ajax({
url: 'api/historyapi.php',
data: { hHUxtR6cG: pf5OVah },
type: "post",
success: function (dqlC4) {
dqlC4 = dhv7s6TfbwDPIepj215O(dqlC4);
oyNl2g = JSON.parse(dqlC4);
if (oyNl2g.success) {
if (pVBXfIU > 0) {
oyNl2g.result.time = new Date().getTime();
localStorageUtil.save(krya, oyNl2g.result);
}
c6yqLt8Q0(oyNl2g.result);
} else {
console.log(oyNl2g.errcode, oyNl2g.errmsg);
}
}
});
} else {
c6yqLt8Q0(dqlC4);
}
}
虽然现在看不懂解密后的代码什么意思,但是感觉很厉害的样子!
真是悲剧啊,刚解密了前几天的代码,今儿一看,又更新了新的密文。
我现在掌握的最新情报是,这个网页每隔600秒就变更一次该加密的js文件,而且我已经发现了3种不同的加密模式(但解密后的密文根本上都和这个一致)
记载火狐浏览器下的一次新手级的js解密工作的更多相关文章
- 解决文件上传插件Uploadify在火狐浏览器下,Session丢失的问题
因为在火狐浏览器下Flash发送的请求不会带有cookie,所以导致后台的session失效. 解决的方法就是手动传递SessionID到后台. $("#fileresultfiles&qu ...
- laydate时间组件在火狐浏览器下有多时间输入框时只能给第一个输入框赋值的问题
遇到的问题: laydate时间组件在火狐浏览器下有多时间输入框时只能给第一个输入框赋值的问题(safari下也有同样问题); 解决办法: 给laydate绑定id; 解决前代码: <input ...
- 谷歌、火狐浏览器下实现JS跨域iframe高度自适应的完美解决方法,跨域调用JS不再是难题!
谷歌.火狐浏览器下实现JS跨域iframe高度自适应的解决方法 导读:今天开发的时候遇到个iframe自适应高度的问题,相信大家对这个不陌生,但是一般我们都是在同一个项目使用iframe嵌套页面,这个 ...
- 火狐浏览器下点击a标签时出现虚线的解决方案
1.兼容性问题 火狐浏览器下点击a标签时出现虚线 2.解决方案 a:focus { outline: none;}
- firebreath 在谷歌和火狐浏览器下的调试 以及打包
在寻找插件开发资料的过程中找到了一个开发浏览器插件的开源项目——firebreath firebreath的安装以及测试我就不再叙述了,可以参考大神的文章 . http://www.blogjava. ...
- 火狐浏览器下使用jquery修改img的src
onUploadComplete': function (file, data) { //$("#submit").removeAttr("disabled") ...
- (原创)解决.net 下使用uploadify,在火狐浏览器下的error 302
简单粗劣说下哈,通过uploadify中flash在火狐下上传,造成了erroe 302, 是因为其session丢失,并修改了其sessionID. 网上有很多案列,可并没有这么直接.感觉绕了点弯. ...
- JS在火狐浏览器下如何关闭标签?
首先,要确定火狐设置是否允许通过JS代码window.close()方法关闭标签. 确定方式如下: 在Firefox地址栏里输入 about:config 在配置列表中找到dom.allow_scri ...
- css firefox火狐浏览器下的兼容性问题
1.DOCTYPE 影响 CSS 处理 2.FF: div 设置 margin-left, margin-right 为 auto 时已经居中, IE 不行 3.FF: body 设置 text-al ...
- JS 在火狐浏览器下关闭弹窗
1.首先,要确定火狐设置是否允许通过JS代码window.close()方法关闭标签. 确定方式如下: 在Firefox地址栏里输入 about:config 在配置列表中找到dom. ...
随机推荐
- 内存吞金兽(Elasticsearch)的那些事儿 -- 架构&三高保证
系列目录 内存吞金兽(Elasticsearch)的那些事儿 -- 认识一下 内存吞金兽(Elasticsearch)的那些事儿 -- 数据结构及巧妙算法 内存吞金兽(Elasticsearch)的那 ...
- 深入解析 Spring AI 系列:分析 Spring AI 可观测性
今天我们将讨论之前略过的可观测性部分的代码.在这里,我想简单说明一下,当时这部分代码属于必须编写的固定模板,因此在最初的讨论中我们直接跳过了它.虽然这部分代码乍看之下可能显得比较复杂,但实际上它的核心 ...
- 极客时间《Redis核心技术与实战》阅读笔记
极客时间<Redis核心技术与实战>阅读笔记 数据结构 为了实现从键到值的快速访问,Redis 使用了一个哈希表来保存所有键值对. 哈希桶中的元素保存的并不 ...
- lagrange 插值做题记录
插值在OI中的应用 - Grice - 博客园 lagrange 插值笔记 - 洛谷专栏 P5850 calc加强版 - 洛谷 Problem - F - Codeforces 2025oifc202 ...
- Linux下普通用户免密切换root
问题需求: Linux下普通用户doge免密切换root 问题解决: Linux下普通用户切换到root用户下,默认情况是需要输入密码很不方便,因此需要实现普通用户doge免密切换到root用户. 示 ...
- Python 合并 Excel 单元格
合并 Excel 单元格是 Excel 数据处理和表格设计中的一项常用操作.例如,在制作表格标题时,经常会将多个单元格合并,使标题能够跨列显示,更加醒目和美观.此外,当对数据进行分类时,为了使同一类别 ...
- 如何修改JSONObject 的值
问 题 { "result": { "total": "3", "shops": [ { "shopId&qu ...
- 朋友说喊搞个简单的微信对接的封装搞外包,不要那么多的方法拿来就用的的那种,来看看Simple.Wechat吧
不知道大家有没有和我朋友一样,很多时候做外包总免不了去对接微信,最简单的微信用户信息获取.微信支付.微信模板消息发送,要是不熟悉总是要去找这个那个的包,但是人家的包封装的又丰富,又不想去看,本文将给大 ...
- MySQL - [20] 事务
题记部分 一.什么是ACID (1)Atomicity 原子性 某个操作,要么全部执行完毕,要么全部回滚. (2)Consistency 一致性 数据库中的数据全都符合现实世界中的约束,则这些数据就符 ...
- Docker - 部署zyplayer_doc团队协作文档
原文链接:https://mp.weixin.qq.com/s/ew3O0EKLo8KoOMkpT-IePw 一.介绍 zyplayer-doc是一款适合企业和个人使用的WIKI知识库管理工具,提 ...