ua.js中UA生成函数vq4的“庐山真面目”
actionlog_js_ua.js混淆编程形式与执行主流程
ua.js中mousedown和mousemove事件自定义函数功能解析
ua.js中JSocket.getlso和JSocket.setlso代码分析
ua.js中arguments.callee.caller的应用
前面几篇文章主要介绍了参与生成UA的信息与事件类型,有关注此系列文章的同学们会发现每一种信息或事件处理处理完成后,都会将相关信息作为参数传递给函数vq4并调用vq4。没错,vq4就是ua.js中生成UA的函数,本文分析该函数生成UA的原理。
vq4函数主体部分的执行条件是zv=true,即load函数必须已执行,其主要作用是根据当前事件信息生成并更新UA值,并将更新后的UA赋给网页中ID为UA_InputId的元素和UA_opt [“LogVal”]。我们先给出vq4经过反混淆与简化之后的代码,然后根据该函数的执行流程来逐步解析该UA生成函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
vq4 = function (t9tl) { var ad9 = t9tl; //DOM加载完成时,该标志位会置1 if (!zv) { return; } m2x(ovh("" + arguments[callee][caller]), "s"); var nwh = []; var i = 0; for(; i < ad9.length; i ++){ nwh.push(ad9[i]);//将传入的数组对象中的元素依次存入数组nwh中 } u69(t9tl); xu.push(nwh); if((ivsz["SendMethod"] & 1) > 0){ wsa(ad9);////给ID为UA_InputId的元素值赋值加密字符串 } if((ivsz["SendMethod"] & 2) > 0){ kx(); } if((ivsz["SendMethod"] & 4) > 0){ sj82(ad9); } if((ivsz["SendMethod"] & 8) > 0){ xy(ad9);//给UA_opt["LogVal"]赋值 } }; |
1. 调用函数m2x
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
ovh = function (er3k) { er3k = er3k["replace"](/[^g-km-svwyzG-KM-SVWYZ_$=]/g, ""); var sk2 = 31, i = 0, isr = er3k["length"]; while (i < isr) { //charCodeAt: 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数 sk2 ^= (sk2 << 5) + (sk2 >> 2) + er3k["charCodeAt"](i++); } return sk2; }; m2x = function (hnp1, l4xy) { for (var i = 0, isr = uv5["length"]; i < isr; i++) { if (hnp1 == uv5[i]) { return true; } } f1(hnp1, l4xy); return false; }; f1 = function (yu6, v7tw) { f(["ic", yu6, v7tw]); gby(); }; f = function (d) { su(wjt(ovh("" + arguments["callee"]), "" + arguments["callee"])); var rz6i = 20; vq4[20,d]; }; |
在获取到当前调用vq4的函数名之后,先用ovh对函数名进行加密,然后调用函数m2x。在函数m2x中出现一个变量名为uv5,该变量与函数su息息相关,后续会进一步说明,这里我们先认为uv5中存储的是函数名,这些函数均与UA相关,从DOM加载完成开始,按执行顺序依次存储。
了解这一点之后,我们就能明白m2x函数的作用:判断vq4当前的调用者是否已经存储在uv5中,若已存储,则do nothing并返回true;若不存在,则调用函数f1,并返回false。但是,从ua.js文件全局来看,vq4与su函数全部是成对出现,且su均在vq4之前执行,因此vq4的调用函数必定存在于uv5中。所以如果我没有理解错误的话,f1函数是不会执行的,也就是说执行的函数名不会参与生成UA。
后面我们在讲述su函数时,会提到调用f1时是一个异常现象,函数f1会用当前vq4的调用函数名去参与更新UA,然后会调用异常处理函数gby()。
2.将vq4传入的参数依次存入数组nwh中
3.调用函数u69
1 2 3 4 5 6 7 |
u69 = function (tu) { var q7xo = tu[0]; var nf = q7xo; ab5["push"](q7xo * nf - ab5["length"]); }; |
针对不同的事件,函数vq4参数数组的第一个元素均是一个唯一的编号。函数u69用该唯一编号与参数的长度计算出一个特征值存储在全局变量ab5中。
4.将存储参数的数组nwh存入全局变量xu中
5.有条件调用函数wsa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
wsa = function (wynf) { if(typeof ivsz['FormId'] != string){ return; } var v3 = document.getElementById("UA_InputId"); if (!v3) { return; } //判断document.getElementById("UA_InputId").value该值中"|"的个数 var ixpk = xe(v3.value) + 1; ////将传入参数进行多重加密后返回 当前次鼠标或键盘动作信息的加密数据 var wz1 = lbet(wynf, ixpk); //参数ab5中存储的是从页面加载到本次调用时所有的鼠标或键盘事件对象的相关信息,zzs为此类信息的加密数据 var zzs = lbet(ab5, ixpk + 1); if("" == v3.value){ v3.value = ulqc(xl6q.substring(53, 56) + wz1 + "|" + zzs); }else{ var kzf = v3.value.lastIndexOf("|"); var a3g = v3.value.substring(0,kzf); //每一次更新"UA_InputId"的值时,会先去除上一次更新值的最后一个"|"的内容,然后再添加本次的新内容 v3.value = ulqc(a3g + "|" + wz1 + "|" + zzs); } }; |
wsa将当前事件按一定规则加密后赋值给ID为UA_InputId的元素,事件信息加密分为两部分:假定N为当前UA_InputId值中“|”的数量,ad9为当前参与更新的信息,加密一:str1 = lbet(ad9, N + 1);加密二:str2 = lbet(ab5, N + 2)。由代码可知,ID为UA_InputId元素的值每一次更新都会将上一次更新信息的str2剔除后再添加本次更新信息的值。这样我就又有个疑问了:ua.js什么时候开始执行呢?应该是淘宝登录页面加载完成的时候吧。那么密码输错几次登录时UA字串应该会比密码一次输入成功的要长很多吧?
6. 有条件调用函数kx
1 2 3 4 5 6 7 |
kx = function () { qjgl = ((typeof ivsz['SendInterval'] == number) && (ivsz['SendInterval'] > 0))?ivsz['SendInterval']:1; if (xu["length"] >= qjgl) { c5k(); }; }; |
这里会调用函数c5k记录页面访问统计信息,后续会深入说明函数c5k。
7. 有条件调用函数sj82
1 |
sj82 = function (b0z) {}; |
函数sj82其实什么都没做。
8. 有条件调用函数xy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
xy = function (b0z) {//b0z: xy函数调用时传入的是当前鼠标或键盘动作信息的加密字串 if (ivsz["LogVal"]) { var szmi = window.eval("ivsz[LogVal]"); var ixpk = xe(szmi) + 1;//ixpk存储szmi中"|"的个数 var wz1 = lbet(b0z, ixpk); var zzs = lbet(ab5, ixpk + 1); if (szmi == "") { szmi = xl6q.substring(53, 56) + wz1 + "|" + zzs; } else { var kzf = szmi.lastIndexOf("|"); var a3g = szmi.substring(0,kzf); //每一次更新时,会先去除上一次更新值的最后一个"|"的内容,然后再添加本次的新内容 szmi = a3g + "|" + wz1 + "|" + zzs; } //对szmi进行一定规则的运算后返回 szmi = ulqc(szmi);//与赋给UA_InputId的值类似 //给UA_opt ["LogVal"]赋值, window.eval("ivsz["LogVal"]="+ szmi + ';'); } }; |
函数xy与函数wsa的功能类似,将事件信息加密后用于生成和更新UA,更新的原则与wsa相同。只是xy函数将生成的UA赋值给UA_opt [“LogVal”]。