前阵子,在奉命抓取某个网站的数据时,发现了一种前端JS加密校验DES—CBC模式。当时觉得头都大了,查了很多资料,瞎琢磨了一天,终于把它破解了,虽然方法愚蠢了点,但总算达到了目的。
JS的加密参数类似:uewZMOSGPb0Wu2KBbF5LxMcqMRzMjrAh8rGiJamd/MyKGoGRCoy4Awe7zRcO412qflhbUinG1f#2BBXlrIKGULSSU7hRYQQcJKp65YtKZH58CowbDIK7ROdFMQv3QrFqSj0pzihUkAijsgHG2UCsOE1AxkxemU/0Rd/iOw38ti0GDdexaaXsxxFnlrZrxZLzmK7zV7t0g50wblbL/TlWdv7mmPt2EiMwOAFhof7EbkPBfaw6vQ06YuKbHQQmvS24jQFw7eum6QUf9f5L2Cj5O7LGwhfNiBpcjEedpYUGJTkd6IwnKP/Z3XBKy8Ogib5zcmdcH#2B0wl1TXwotrsYzyaaWA==
好吧,确实是一段很长的密文
走个分析流程吧:
点击该网站内的查询按钮时,调用的是searchFlight这个方法:
e.searchFlight = function() {
var t = M(),
n = t.replace(/\%/g, "#");
x() && (E(), e.isBargain = !1, e.desc = n, e.pageData = T(), e.routerJson = k(), e.flights = null, e.$emit("$loading"), o.get("/pssweb/ota/flights", {
params: {
//省略部分参数
desc: e.desc
}
}).success(function(t) {
if (e.$emit("$loading-finish"), t.flihtProductList) {
var n = F(t.flihtProductList);
"OW" == e.pageData.flightWayType ? P(n) : L(n)
}
}).error(function() {
e.$emit("$loading-finish")
}))
}
其中加密参数desc 产生的关系大概是 desc = e.desc = n = t = M();
根据M(),找到方法:
M = function() {
var e = (new born).getCiphertext();
return e
};
以及下方的实现流程:
window.mcArrs = [];
var m = "000215455";
window.mcArrps = [],
document.onkeypress = keypress,
document.onkeydown = keydown;
var mc = function() {
jQuery && jQuery.mouseCollection.record(function(e) {
var t = JSON.stringify(e.currentPoint);
mcArrs.length < 5 ? mcArrs.push(t) : (mcArrs.shift(), mcArrs.push(t))
})
}
function getScriptPaths() {
m = "52D2841A3485DFFBCF2EA6A0515077CD";
var e = document.scripts;
return e = e[e.length - 1].src.substring(0, e[e.length - 1].src.lastIndexOf("/") + 1)
}
born.prototype = {
getCiphertext: function() {
return getParams()
}
},
function getParams() {
var e = {
xy: mcArrs,
fingerprint: (new referFingerprint).get()
};
mcArrs = [];
var t = JSON.stringify(e),
r = eq_u(t, m);
return del_html_tags(r, "\\+", "%2B")
}
function eq_u(e, t) {
var r = coypJsDk.enc.Utf8.parse(t),
n = coypJsDk.DES.encrypt(e, r, {
mode: coypJsDk.mode.CBC,
padding: coypJsDk.pad.Nopk
});
return n.toString()
}
从以上代码已经很清晰地发现所有的加密参数, 此种js加密方式,是取得鼠标在一段时间内,经过屏幕的轨迹坐标,再借由DES加密组成一串密文。其中McArrs是鼠标轨迹坐标数组,fingerprint则是浏览器的身份(自己这么认为,啊哈哈!可以自己查询fingerprint.js了解一下),JSON.stringify就无需解释了。关键点是上方eq_u()方法的加密。
截止目前,我们所需要取到的数据便是McArrs以及fingerprint这两个参数,fingerprint可以随便取个浏览器生成,McArrs也可以利用js模拟鼠标轨迹生成。至于m这个参数,是从getScriptPaths()此方法取到的。
此时需要改动原JS,将不必要的代码去除,只留下相关加密js,然后利用Java代码(因为是利用HttpClient抓取的-_-)调用js生成加密参数即可。
给该修改后的JS添加方法:
function encryptByDESModeCBC(fingerid){
var t1 = Date.parse(new Date());
var t2 = parseInt(t1)+210;
var t3 = parseInt(t2)+230;
var x1 = Math.floor(Math.random()*40+30);
var x2 = Math.floor(Math.random()*40+30);
var x3 = Math.floor(Math.random()*40+30);
var y1 = Math.floor(Math.random()*40+30);
var y2 = Math.floor(Math.random()*40+30);
var y3 = Math.floor(Math.random()*40+30);
mcArrs.push("{\"x\\\":"+x1+",\\\"y\\\":"+y1+",\\\"t\\\":"+t1+"}");
mcArrs.push("{\"x\\\":"+x2+",\\\"y\\\":"+y2+",\\\"t\\\":"+t2+"}");
mcArrs.push("{\"x\\\":"+x3+",\\\"y\\\":"+y3+",\\\"t\\\":"+t3+"}");
var e = {
xy: mcArrs,
fingerprint: fingerid
};
mcArrs = [];
var message = stringify(e);
var m = eq_u(message,key);
return m;
}
随机获取的x1,y1参数作为x,y坐标(选取三个
),t1为时间戳,t2,t3的时间戳加上一些延时,达到仿真轨迹。fingerprintid 取谷歌浏览器的1814798975 (可以自己生成),利用后端JAVA代码调JS的方式:
public static String getDesc(){
String desc = "";
try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
String path = FingerPrintUtil.class.getResource("/").getPath()+"service.js";
FileInputStream fileInputStream = new FileInputStream(new File(path));
Reader reader = new InputStreamReader(fileInputStream, "utf-8");
engine.eval(reader);
if(engine instanceof Invocable) {
Invocable invoke = (Invocable)engine;
desc = (String) invoke.invokeFunction("encryptByDESModeCBC", new Object[]{fingerid});
}
reader.close();
return desc;
} catch (FileNotFoundException e) {
logger.error(e.getMessage(),e);
} catch (ScriptException e) {
logger.error(e.getMessage(),e);
} catch (NoSuchMethodException e) {
logger.error(e.getMessage(),e);
} catch (IOException e) {
logger.error(e.getMessage(),e);
}
return "";
}
返回的desc,即是生成的密文,到这就可以进行下面的抓取工作了。
不过,不道德的开大量线程抓取数据,加大了对方服务器压力,真是罪该万死(---)。