闲逛github看到issues别人发的一个网站,网址:aHR0cCUzQS8veXMuZmdqLnRhaXl1YW4uZ292LmNuL0ZpcnN0aGFuZC90eWZjL3B1Ymxpc2gvcC9Qcm9qZWN0TGlzdC5kbw==
随便勾选菜单查询,上抓包 如下图requestbody
分析一下requestbody的生成过程,老规矩F12
匿名函数的无限debugger,直接删掉debugger代码即可,有2个debugger暗桩。分析下把2个都干掉,然后替换JS文件即可
查询下断到这儿 看到了熟悉的CryptoJS.AES.encrypt,以为本次分析就此结束了。但是简单枯燥的生活,还是来扣一下整个JS吧
扣JS把CryptoJS整个类扣取出来即可 直接搜AES的定义可以快速定位,AES的16进制字符为’\x41\x45\x53’
扣取的过程中会发现一个内存溢出RangeError: Maximum call stack size exceeded
提取出来,分析一下这段代码
var _0x3b5fab = function () {
var _0x142e66 = !![];
return function (_0xb9d633, _0x7fe927) {
var _0x2bc370 = _0x142e66 ? function () {
if (_0x7fe927) {
var _0x42c9f5 = _0x7fe927['apply'](_0xb9d633, arguments);
_0x7fe927 = null;
return _0x42c9f5;
}
}
: function () { }
;
_0x142e66 = ![];
return _0x2bc370;
}
;
}();
var _0x2da5d7 = _0x3b5fab(this, function () {
var _0x15430c = function () {
return '\x64\x65\x76';
}
, _0x439f7e = function () {
return '\x77\x69\x6e\x64\x6f\x77';
};
var _0x56e3d2 = function () {
var _0x4f3652 = new RegExp('\x5c\x77\x2b\x20\x2a\x5c\x28\x5c\x29\x20\x2a\x7b\x5c\x77\x2b\x20\x2a\x5b\x27\x7c\x22\x5d\x2e\x2b\x5b\x27\x7c\x22\x5d\x3b\x3f\x20\x2a\x7d');
return !_0x4f3652['\x74\x65\x73\x74'](_0x15430c['\x74\x6f\x53\x74\x72\x69\x6e\x67']());
};
var _0x27b501 = function () {
var _0x57a526 = new RegExp('\x28\x5c\x5c\x5b\x78\x7c\x75\x5d\x28\x5c\x77\x29\x7b\x32\x2c\x34\x7d\x29\x2b');
return _0x57a526['\x74\x65\x73\x74'](_0x439f7e['\x74\x6f\x53\x74\x72\x69\x6e\x67']());
};
var _0x574716 = function (_0x14e333) {
var _0x16e1a5 = ~-0x1 >> 0x1 + 0xff % 0x0;
if (_0x14e333['\x69\x6e\x64\x65\x78\x4f\x66']('\x69' === _0x16e1a5)) {
_0x5f28c6(_0x14e333);
}
};
var _0x5f28c6 = function (_0x2e24f4) {
var _0xc07f1b = ~-0x4 >> 0x1 + 0xff % 0x0;
if (_0x2e24f4['\x69\x6e\x64\x65\x78\x4f\x66']((!![] + '')[0x3]) !== _0xc07f1b) {
_0x574716(_0x2e24f4);
}
};
if (!_0x56e3d2()) {
if (!_0x27b501()) {
_0x574716('\x69\x6e\x64\u0435\x78\x4f\x66');
} else {
_0x574716('\x69\x6e\x64\x65\x78\x4f\x66');
}
} else {
_0x574716('\x69\x6e\x64\u0435\x78\x4f\x66');
}
});
_0x2da5d7();
!_0x56e3d2()为假是就会进入_0x574716 和_0x5f28c6 死循环中,导致堆栈溢出
_0x56e3d2函数,正则_0x15430c是否匹配
var _0x4f3652 = new RegExp('\w+ *\(\) *{\w+ *['|"].+['|"];? *}');
return !_0x4f3652.test(_0x15430c.toString());
通过判断函数是否格式化来判定代码是否在调试状态,因为很多格式化工具会添加换行等,原生态环境中_0x15430c 应该为
var _0x15430c=function(){return '\x64\x65\x76';}
格式化后代码:
var _0x15430c = function () {
return '\x64\x65\x76';
}
熟悉了套路,直接删掉此段代码即可,后面扣除代码即可。AES的key动态,主要代码如下
var parm="PerYear=&PerFlowNO=&PerType=0&ProType=1&ProName=&OrgName=&HouseType=0&Region=0&ProAddress=&pageNo=1&pageSize=15TAIL";
var _0x56924a=xhs_0x3daa52(0x8);
var _0x2da736=crc32(_0x56924a);
while (_0x2da736[xhs__0x8016('0xf4')](0x0) == '\x2d' || _0x7a0d75['\x53\x4d\x5a\x79\x4d'](_0x7a0d75['\x6e\x63\x63\x4f\x4c'](_0x56924a['\x63\x68\x61\x72\x41\x74'](0x0), 0x2), 0x1)) {
_0x56924a = xhs_0x3daa52(0x8);
_0x2da736 = crc32(_0x56924a);
}
_0x56924a+=_0x2da736;
var _0x227f6e = this.CryptoJS.enc['\x55\x74\x66\x38']['parse'](_0x56924a);
var result= _0x7a0d75[xhs__0x8016('0x1a7')](_0x56924a,this.CryptoJS.AES.encrypt(parm, _0x227f6e, {
'\x6d\x6f\x64\x65': this.CryptoJS[xhs__0x8016('0xda')][xhs__0x8016('0xdb')],
'\x70\x61\x64\x64\x69\x6e\x67': this.CryptoJS['\x70\x61\x64'][xhs__0x8016('0xdd')]
}));
console.log(result);
本文仅作为技术讨论与分享,严禁用于非法用途,不接单,打发枯燥生活,欢迎提供素材:321481996@qq.com