下面对抢购实现的几个功能类,进行分析。说明欺骗存在的地方
通过逐行的分析,小米的骗局越来越清晰了。其实页面就30秒被提交一次。提交后获得一个js文件,其js文件的地址类似是http://tc.hd.xiaomi.com/hdget?callback=hdcontrol&_=hdget,然后
hdcontrol({
"stime": 1378180861,
"status": {
"allow": false,
"miphone": {
"hdstart": true,
"hdstop": false,
"hdurl": "?_a=20130903_september_first_weeks&_op=choose",
"duration": null,
"pmstart": false
},
"mibox":
{
"hdstart": true,
"hdstop": false,
"hdurl": "?_a=20130903_september_first_weeks&_op=choose",
"duration": null,
"pmstart": false
}
}
})
这个文件竟然直接调用下面写的hdcontrol函数。还直接传了参。。。
e里面保存了隐藏的hdurl,如果大家知道url可以直接进入选择页面,当然请求的时候应该服务器要认可预定成功的判断。
所以,骗局在于页面重试是30秒才重新抢一次的,用户之后点多少次都没用。所谓的倒计时也是在骗人。
这个是一个url捉迷藏的游戏。
先去吃饭,下午继续。
如果真心想抢到小米的东西,开无数个窗口,然后点了抢购之后,安心等待30秒是一个好办法。如果有有一个页面抢到了,页面会自己跳转的。
function hdcontrol(e) {
var c = e.status, //e.status是服务器端返回的一个json,最后分析这个json
d = c.miphone.hdurl,
b = c.mibox.hdurl,
a = c.mitv.hdurl,
f = e.d22a51 ? e.d22a51 * 0x3e8: 0x1388; //是5秒还是1秒,是一个问题, d22a51 是什么?
if (CONFIG.proType === ”phone“ && d) { //你可以买手机了
m.locationNext(d)
} else {
if (CONFIG.proType === ”box“ && b) { //你可以买盒子了
m.locationNext(b)
} else {
if (CONFIG.proType === ”tv“&& a) { //你可以买电视了
m.locationNext(a)
} else {
if (window.ajaxInter) {
clearTimeout(ajaxInter) //清除延时
};
ajaxInter = window.setTimeout(function() {
getStatus.requestAgain() //设定重试的延时
},
f) //延时时间是1秒或者5秒
}
}
};
getPermit.hdStatus(e) //下面再看
};
function hdinfo(a) {
var b = a.stime + (0x8 - new Date().getTimezoneOffset() / 0x3c * -0x1) * 0xe10; //服务器返回的当前时间
servertime = b; //结合时区存起来
CONFIG.isBook = { //记录是否预订了
phone: a.status.miphone.reg,
tv: a.status.mitv.reg,
box: a.status.mibox.reg
};
getPermit.hdStatus(a)
};
var getPermit = {
hdStatus: function(c) {
var a = c.status,
e = (a.miphone.hdstart === false && a.miphone.hdstop === true) ? true: false, //查看返回结果,判断是否在购买活动期间,每次应该是10分钟左右。最少应该是5分钟,几分钟后,活动结束。
b = (a.mibox.hdstart === false && a.mibox.hdstop === true) ? true: false, //盒子
d = (a.mitv.hdstart === false && a.mitv.hdstop === true) ? true: false; //电视
if (d || e || b) {
window.clearTimeout(timeoutRequest); //获得服务器时间,如果不在活动内,就获取服务器时间
m.$(”preLoad“).style.display = ”none“;
if (window.getServerTime) {
window.clearInterval(getServerTime)
};
m.$(”hdTipInfo“).style.display =”none“;
m.$(”showMore“).style.display =”none“
};
if ((d && CONFIG.proType === ”tv“) || (e && CONFIG.proType === ”phone“) || (b && CONFIG.proType === ”box“)) {
getStatus.boxy(false, ”“);
htmlStr.getOverInfo(); //查看预订类型
getStatus.boxy(true, ”-reg“)
};
if (d && e && b) {
m.cookieXM(CONFIG.cookies.status, 0x1); //存储到cookie里面
htmlStr.init(0x4) //4
} else {
if (d && e && !b) {
htmlStr.init(0x7) //7是买手机,电视
} else {
if (d && !e && !b) {
htmlStr.init(0x5) //5是买电视
} else {
if (d && !e && b) {
htmlStr.init(0x8) //8是买盒子和电视
} else {
if (!d && e && b) {
htmlStr.init(0x9) //9是买手机,盒子
} else {
if (!d && !e && b) {
htmlStr.init(0xa) //10是盒子
} else {
if (!d && e && !b) {
htmlStr.init(0x6) //是手机
}
}
}
}
}
}
}
}
},
//
getStatus = {
jsonInter: function(c, d, a) {
var b = new Date().getTime(); //构建一个js块,其实小米通过js块的src=部分,插入js块后,向服务器提交购买请求
m.creatJs(c + b, d, a)
},
init: function() {
var a = this;
if (m.cookie(CONFIG.cookies.status)) {
m.$(“preLoad“).style.display =”none”;
m.$("hdTipInfo ").style.display = "none";
m.$("showMore").style.display = "none";
htmlStr.init(0x4); //4,都买。。
return false
};
this.jsonInter(CONFIG.srcs.hdinfo, “hdinfo”, false); //在页面初始化完毕的时候,就查询信息,比如服务器时间,是否登录,是否预约
getServerTime = window.setInterval(function() {
if (typeof(servertime) !== “undefined”) {
window.clearTimeout(timeoutRequest);
m.$(“preLoad”).style.display = “none”;
a.jugeStatus(); //下面再分析
window.clearInterval(getServerTime)
}
},
CONFIG.timer.server); //0.1秒之后就执行
timeoutRequest = setTimeout(function() {
m.$(“preLoad”).innerHTML = “ <p>抱歉,网络拥堵无法连接服务器。由于访问人数太多导致服务器压力山大,请刷新页面重试~”;
m.$(“preLoad”).style.cssText =“background:none;”
},
CONFIG.timer.timeout) //5秒后是超时了。
},
jugeStatus: function() { //举个?
var d = this,
c = servertime * 0x3e8, //服务器时间*1000
b = CONFIG.startDate, //抢购开始时间
a = b - CONFIG.preLogMin * 0xea60; //30*60000是30分钟
Util.resetServertime(); //重新读取服务器时间,校准时间,确实会出现时间不准的现象
if (c < a) { //服务器时间还没到
htmlStr.init(0x1); //显示没开始抢购
d.timeOut()
} else {
if (c >= a && c < b) { //当进入最后30分钟
htmlStr.init(0x2); //进入状态2
if (m.cookie(CONFIG.cookies.userid)) {
d.timeOut()
}
} else {
if (c >= b) {
htmlStr.init(0x3); //到点了,开始抢购
if (window.resetTime) {
clearInterval(resetTime)
};
m.$(“hdTipInfo”).style.display =“none”; //倒计时版消失
m.$(“showMore”).style.display = “ none ”
}
}
}
},
timeOut: function() { //如果没到时间,继续倒计时
var a = this;
a.setRemainTime(); //显示倒计时的时间
if (window.InterValObj) {
clearInterval(InterValObj)
};
InterValObj = window.setInterval(function() {
a.setRemainTime()
},
CONFIG.timer.status) //以1秒为更新频率的倒计时
},
setRemainTime: function() {
var e = this,
b = CONFIG.startDate,
h = b / 0x3e8;
surplusTime = h - servertime;
if (surplusTime >= 0x0) {
var c = Math.floor(surplusTime % 0x3c),
f = Math.floor((surplusTime / 0x3c) % 0x3c),
d = Math.floor((surplusTime / 0xe10) % 0x18),
g = Math.floor((surplusTime / 0x15180) % 0x1e);
servertime++;
var i = [c.toString(), f.toString(), d.toString(), g.toString()];
var a = “<span>” + i[0x2] + “ </ins>小时” + i[0x1] + “</ins>分” + i[0x0] + “</ins>秒后开始”;
if (d === 0x0) { //最后一小时
a = “<span>” + i[0x1] + “</ins>分”+ i[0x0] +“</ins>秒后开始”;
if (f === 0x0) {
a = _$[104] + i[0x0] + “</ins>秒后开始”//最后一分钟的显示
}
};
if ( !! m.$ (“ surTime ”) {
m.$ (“ surTime ”).innerHTML = a
}
} else {
m.$(“surTime”).innerHTML =“正在加载中...”;
setTimeout(function() {
htmlStr.init(0x3) //显示抢购的按钮,可以点抢购按钮了
},
0x7d0); //又浪费2秒钟。。如果按标准流程,要慢2秒才能开始抢购
if (window.InterValObj) {
clearInterval(InterValObj)
}
}
},
//以上就是为了显示倒计时的时间
boxy: function(b, d) {
var c = (d) ? d: “”,
a = m.$("boxbg"),
e = m.$("box" + c);
if (b) {
a.style.height = Math.max(document.documentElement.clientHeight, document.documentElement.scrollHeight) + "px";
a.style.display = "block";
e.style.display = "block";
if (!d && !window.loadingAnimate) {
Util.animate(“LoadingAnimate”, 0x7d0) //2秒钟的动画
}
} else {
a.style.display = “none”;
e.style.display = _“none”;
if (!d && window.ajaxInter) {
clearTimeout(ajaxInter)
}
}
},
requestAgain: function() { //再次请求src里面的地址,但是隔了30秒
var a = this;
a.jsonInter(CONFIG.srcs.hdget,“hdget”, true)
}
},
htmlStr = {
init: function(a) {
m.$(“hdBtns”).innerHTML = this.btn(a);
m.$(“hdSubTitle”).innerHTML = this.subTitle(a);
m.$(“hdLnks”).innerHTML = this.links(a);
m.$(“hdMsg”).innerHTML = this.msgs(a);
m.$(“mi3btn”).innerHTML = this.phoneBtn(a);
m.$(“mitvbtn”).innerHTML = this.tvBtn(a);
if (a === 0x4) {
m.$(“linksCon-span”).innerHTML = “购买查询”
}
},
btn: function(d) {
var c = “<a class="btn" onClick="Util.showBox('phone');">购买手机”,
i = "<a class="btn" onClick="Util.showBox('tv');">购买电视",
f = "<a class="btn" onClick="Util.showBox('box');">购买盒子",
j = m.cookie("xm_pf_wl") ?"<a class="btn" href="" + CONFIG.urls.phone + "" onClick="return Util.bookedPop('phone',this);">支付手机</a>": "<a class="btn" href="" + CONFIG.urls.bookPhone +"" target="_blank">预约手机</a>", //这里注意,订购成功后,小米会更新客户端cookie的xm_pf_wl的字段,只有xm_pf_wl为1,才显示支付按钮,不过我们可以直接输入支付网页,或者刷新页面获得支付状态对cookie的更新
h = m.cookie(“xm_pft_wl ”) ? “<a class="btn" href="” + CONFIG.urls.tv + “" onClick="return Util.bookedPop('tv',this);">支付电视</a>” : “<a class="btn" href="” + CONFIG.urls.bookTv + “" target="_blank">预约电视</a>”, //同上
g = m.cookie(“xm_pfb_wl”) ? “<a class="btn" href="” + CONFIG.urls.box + “" onClick="return Util.bookedPop('box',this);">支付盒子</a>” : “<a class="btn" href="” + CONFIG.urls.bookBox + “" target="_blank">预约盒子</a>”,
e = “<span class="untime">正在加载中...”,
b = “ <a class="btn" href="” + CONFIG.urls.login +”" title="提前登录" >提前登录</a>“;
//以下显示不同的按钮
switch (d) {
case 0x1:
return e;
break;
case 0x2:
var a = (m.cookie(CONFIG.cookies.userid));
if (a) {
return e
} else {
return b
};
break;
case 0x3:
return c + i + f; //显示不同的按钮
break;
case 0x4:
return j + h + g;
break;
case 0x5:
return c + h + f;
break;
case 0x6:
return j + i + f;
break;
case 0x7:
return j + h + f;
break;
case 0x8:
return c + h + g;
break;
case 0x9:
return j + i + g;
break;
case 0xa:
return i + i + g;
break
}
},
//以下是子标题
subTitle: function(a) {
var c = “11月19日星期二中午12点开放购买<br>小米3、小米电视、小米2S、红米手机、小米盒子”,
b = “开放购买所有产品已售罄,成功购买用户请尽快支付<br>下午3点将进行剩余订单专场”;
if (a === 0x4) {
return b
} else {
return c
}
},
links: function(b) {
var a = “<a href="”+ CONFIG.urls.checkPhone + “" target="_blank">手机预约查询</a>”+ CONFIG.urls.checkTv + “ <a href=" ”+ CONFIG.urls.checkPhone + “ " target="_blank">+ CONFIG.urls.checkPhone + " target="_blank">手机预约查询</a> ”+,
c =“<a href="” + CONFIG.urls.checkPhoneBuy + “" target="_blank">手机购买查询</a>” + CONFIG.urls.checkTvBuy + “”+ CONFIG.urls.checkBoxBuy +“”
if (b === 0x4) {
return c
} else {
return a
}
},
msgs: function(a) {
var c = “购买成功用户请在2小时内下单,下单后2小时内支付<br/>购买成功用户支付通道:160: " onClick="return Util.bookedPop('phone',this);">支付手机</a>”+ CONFIG.urls.phone +“成功购买用户请在2小时内下单,下单后2小时内支付<br/>红米手机联通合约版首发,¥999起。立即购买” + CONFIG.urls.tv + _$[161] + CONFIG.urls.box + _$[162],
b = “成功购买用户请在2小时内下单,下单后2小时内支付<br/>红米手机联通合约版首发,¥999起。立即购买”;
if (a === 0x4) {
return b
} else {
if (a === 0x1 || a === 0x2) {
return “”
} else {
return c
}
}
},
phoneBtn: function(a) {
var c = “<p class="btn-noStart">1999元(16GB)即将开始”,
d = “<a onClick="Util.showBox('phone');" class="btn-mid" href="#">1999元(16GB)立即购买”,
b = “<a class="btn-mid" href="” + CONFIG.urls.phone +“" onClick="return Util.bookedPop('phone',this);"><span>1999元(16GB)立即支付”;
switch (a) {
case 0x1:
return c;
break;
case 0x2:
return c;
break;
case 0x4:
return b;
break;
case 0x6:
return b;
break;
case 0x7:
return b;
break;
case 0x9:
return b;
break;
default:
return d
}
},
tvBtn: function(a) {
var c = “<p class="btn-noStart">2999元即将开始”,
d = “ <a onClick="Util.showBox('tv');" class="btn-mid" href="#">2999元立即购买”,
b = “ <a class="btn-mid" href="” + CONFIG.urls.tv + “" onClick="return Util.bookedPop('tv',this);"><span>2999元立即支付”;
switch (a) {
case 0x1:
return c;
break;
case 0x2:
return c;
break;
case 0x4:
return b;
break;
case 0x5:
return b;
break;
case 0x7:
return b;
break;
case 0x9:
return b;
break;
default:
return d
}
},
getOverInfo: function() {
var a = “”,
d = “”,
c = “”,
b = function(f, e, g) {
return “<h3>对不起”+ f + “ 都售罄了</h3>都是我不好,人数太多,机器太少。别灰心,你可返回首页尝试购买” + e + “,<br/>也可立即预约11月26日下轮开放购买。” + g + “返回活动首页”
};
switch (CONFIG.proType) {
case “phone”:
a =“小米手机”;
d =“小米电视及小米盒子”;
c = CONFIG.urls.bookPhone;
break;
case “tv”:
a = "小米电视";
d = "小米手机及小米盒子";
c = CONFIG.urls.bookTv;
break;
case "box":
a = "小米盒子";
d = "小米手机及小米电视";
c = CONFIG.urls.bookBox;
break
};
m.$("box-reg-wrap").innerHTML = b(a, d, c);
m.$("box-reg-wrap").style.background = "url(http://p.www.xiaomi.com/open/131101/images/mitu-2.png) no-repeat 5px 0"
}
};
打完,收工。
最后一段分析:
下面这个链接就是抢购用的url
http://tc.hd.xiaomi.com/hdget?callback=hdcontrol&_=当前时间(用javascript写就算是new Date().getTime())
hdget获得请求后,结合cookie里面的内容,roll个结果记录下来,然后通知用户订购的结果
然后这个会返回一个javascript文件,文件里面有去选机型的url,这个文件里面调用了hdcontrol函数,让hdcontrol函数去跳转选机型的url。
这个url开头是http://t.hd.xiaomi.com/s/
然后加上hdcontrol返回的url位置,以开头的例子:
hdcontrol({
"stime": 1378180861,
"status": {
"allow": false,
"miphone": {
"hdstart": true,
"hdstop": false,
"hdurl": "?_a=20130903_september_first_weeks&_op=choose",
"duration": null,
"pmstart": false
},
"mibox":
{
"hdstart": true,
"hdstop": false,
"hdurl": "?_a=20130903_september_first_weeks&_op=choose",
"duration": null,
"pmstart": false
}
}
})
这个url请求最终是http://t.hd.xiaomi.com/s/?_a=20130903_september_first_weeks&_op=choose
这种类似的格式。而将其混淆的url格式读取的代码简单如下即可:
list = []; |
for (var i = 0, l = arr.length; i < l; i++) |
{ |
list[list.length] = '<li>' +i+': '+ arr[i] + ''; |
} |
list = '<ul>' + list.join('') + '</ul>'; |
document.getElementById("copyright").innerHTML=list; |
然后下一步就是抢时间输入验证码了。
还可以考虑直接用firefox或者firebug执行下面抢购的javascript代码,以跳过30秒重试的限制。
getStatus.jsonInter(CONFIG.srcs.hdget, "hdget", true);