那些年携程工程师在代码里下的毒-反爬与反反爬的奇技淫巧
知乎上面的专栏,写出了逆向找到携程的Eleven
参数来伪装浏览器发送ajax,获取携程的酒店房型列表。然而文章并没有给出全部的思路,我们获取到的页面内容内容是酱紫的:
首先还是查看ajax请求网址时
http://hotels.ctrip.com/Domestic/tool/AjaxHote1RoomListForDetai1.aspx?psid=&MasterHotelID=441351&hotel=441351&EDM=F&roomId=&IncludeRoom=&city=2&showspothotel=T&supplier=&IsDecoupleSpotHotelAndGroup=F&contrast=0&brand=0&startDate=2017-08-28&depDate=2017-08-29&IsFlash=F&RequestTravelMoney=F&hsids=&IsJustConfirm=&contyped=0&priceInfo=-1&equip=&filter=&productcode=&couponList=&abForHuaZhu=&defaultLoad=T&TmFromList=F&eleven=c4350e460862b69d9d76724e1325a0a54ef23c2e0648636c855a329418018a85&callback=CASuBCgrghIfIUqemNE&_=1503884369495
的代码, 寻找他的调用栈。
$.ajax(o, {
onsuccess: function(e) {
window[n](e.responseText)//
},
onerror: function() {
window[n] = void 0,
r()
}
}
而执行函数的时候传入的参数就是我们刚刚ajax得到的的json内容。我们打上断点后可以发现e.responseText
为访问页面得到的json转化而成字符串。
而window[n]
方法就在上方
window[n] = function(e) {
window[n] = void 0;
var i = ["T", "F", "e", "s", "M", "a", "g", "r", "o", "B", "u", "l", "y", "m", "f", "t"];
e = $.parseJSON(e),
null == e ? r() : (hotelDomesticConfig.cas.Pretty && e["0|2|3|15|4|2|3|3|5|6|2|1|7|8|13|9|10|15|15|2|7|14|11|12".split("|").map(function(e) {
return i[e]
}).join("")].slice(-9999).indexOf(" ") !== -1 ? e.html = "" : !l && hotelDomesticConfig.cas.decrypt && (e.html = t(e.ComplexHtml, e.ASYS, e.html)),
s(e)),
$('script[src="' + o + '"]').remove()
}
我们在return i[e]
和 $('script[src="' + o + '"]').remove()
处打上断点。执行的时候直接跳过了return i[e]
,我们再看一下各变量的值。
e
原本为传入的e.responseText
的值,既字符串,如今变成了一个对象,浏览一下他的属性。发现有一个html的属性,内容如下:
↵<div class="htl_room_table J_roomTable">↵
<table border="0" cellspacing="0" cellpadding="0" summary="详情页酒店房型列表" id="J_RoomListTbl">↵
<tr>↵
<th class="col1" style="padding-left:10px;">房型</th>↵
<th class="col2"></th>↵
<th class="col3">床型</th>↵
<th class="col5">宽带</th>↵
<th class="col_person">入住人数</th>↵
</i><p>没有符合条件的房型,您可以减少当前筛选项或<a id="J_ShowAllRoomList" href="javascript:void(0);">查看全部房型</a>!</p></td>↵</script>
由于太长我没有吧全部代码放出来。可以看出,这里就是我们想要的结果。
于是我排查了window[n]
里的每一个句子,找到了(e.html = t(e.ComplexHtml, e.ASYS, e.html)
,其中e.ComplexHtml
以及e.ASYS
就是ajax
得到的两个奇形怪状的字段,
跳转到t
函数。
function t(e, t, o) {
var i = "";
if ("function" != typeof NODEJS)
return "";
if ("undefined" == typeof e)
return "";
if (!e)
return "";
try {
i = NODEJS(e, t)
} catch (n) {
i = ""
}
return i
}
return i
的时候,i
已经是html的字符串了。所以我们可以判断就是在方法名为NODEJS
之中完成的解密。
NODEJS = function(n, t) {
var r, o, e = "1", i = void 0 == e[0], c = i ? [] : "";
for (r = 0; r < n.length; r++)
o = t.charAt(n.charAt(r).charCodeAt(0) - 21760).charAt(0),
i ? c.push(o) : c += o;
return c = i ? c.join("") : c
}
把上面的函数e.ComplexHtml
和e.ASYS
丢到node
中执行,结果出来的就是html的字符串。
撒花完结
##总结
写了个python版本的,丢:
def decrypt(n, t):
c = ""
for i in n:
try:
o = t[ord(i) - 21760]
except:
o = ""
c = c + o
return c