地址:http://i.xiaoi.com/
需要:抓包工具(笔者用的是浏览器自带的开发人员工具。快捷键F12)
图 1
在跳转到 url:http://i.xiaoi.com/ 之前打开开发人员工具,跳转之后,发送“测试”二字,得到以下回复
图 2
URL解码之后:
图 3
图 4
可以看到一共有以下参数:callback、sessionId、robotId、userId、body、ts
通过这些参数名我们可以假设:callback的值可能是回调函数名,sessionId、robotId和userId就不用说了,如名所示,body可能是关于所发送内容的相关信息:发送内容和内容类型;ts可能是timestamp的缩写,可能是13位的时间戳
2.接下来验证猜想:
(1)在开发人员工具的搜索栏中搜索“sessionId :”以定位到参数所在的位置
图 5
单击第一次涉及到sessionId的地方,第844行,格式化代码
图 6
从图中可以看到sessionId就是data对象的sessionId属性的值,然后我们往上追溯,看看data的值又是什么
图 7
发现其是作为processOpenResponse函数的一个参数传进来的,而该程序片段是processOpenResponse函数的定义,那么我们看看这个函数又是在哪里被调用的?在开发人员工具的搜索框搜索“processOpenResponse”转到911行,格式化之后
图 8
图 9
该函数是客户端发送请求之后,在接收到返回数据之后进行调用的,而data就是服务器返回的数据。这样的话,那就简单了,直接在开发人员工具的搜索框搜索“sessionId”,为什么搜sessionId不是搜data呢?因为data这个变量名是在太普遍了,一般情况下很多地方都会调用到,为了更加简洁方便地分析,所以这里我选择搜sessionId。过滤掉JS文件之后,发现还是很多,还需要进一步过滤。这里有个思路,由于上文提到data就是服务器返回的数据,当前的数据包URL是http://i.xiaoi.com/robot/webrobot?&callback=__webrobot_processMsg&data=%7B%22sessionId%22%3A%2237360e51d94e4cfcb357e3d6ed9015e1%22%2C%22robotId%22%3A%22webbot%22%2C%22userId%22%3A%2294b8b3aa68524a9cbaab4353c64bc233%22%2C%22body%22%3A%7B%22content%22%3A%22%E6%B5%8B%E8%AF%95%22%7D%2C%22type%22%3A%22txt%22%7D&ts=1560763111783,那么我们在它之前的一些数据包中寻找就行。通过此方法,我们定位到了该URL:http://i.xiaoi.com/robot/webrobot?&callback=__webrobot__processOpenResponse&data=%7B%22type%22%3A%22open%22%7D&ts=1560763093937
图 10
图 11
通过对比这两个包的参数,发现是一致的。这样的话就省事多了,分析一下剩下的参数:callback和ts,先搜索“&ts=”,通过对比URL,发现正确的定位应该是638行,而ts的值也确实是时间戳:
_url += '&callback=' + conf.callback + '&data=' + _data + "&ts=" + new Date().getTime();
图 12
最后就剩callback这个参数的分析了,这里我们不妨在进入638行格式化后在该处下个断点,并在输入框内输入一些字符看看会不会触发到这一个断点上。结果证明,确实断到了。
图 13
图 14
往上追溯发现,conf.callback所在的jsonp函数是fn对象的一个属性,那这样就好办了,直接在该页面搜索一下“fn.jsonp(”,然后在搜到的两处下断点,并在聊天输入框随便输入些字符,看看断到哪里。结果我们看到断到了这里,这里的callback值是写死的,就是__webrobot_processMsg,这与发送消息的包中的callback是一致的。到此为止,发送消息的包中参数已经是分析完了
图 15
我的Chrome有点问题,接下来将使用Firefox进行分析。分析完了URL的参数,接下来就需要分析发送消息的包需要携带的cookie
Cookie: cnonce=688226; sig=792fc785bef5073ae374c2ec525f10e2cb4abfb5; XISESSIONID=gho0x08t7ws11eteyziynepf8; Hm_lvt_822805145daedc4d66ed5fdf3d12cc8b=1560784903; Hm_lpvt_822805145daedc4d66ed5fdf3d12cc8b=1560784903; nonce=428933
图16 抓包显示的cookie
Hm_lvt_8228等模样的cookie通常不需要带上,我之前查了下说是相关的cookie,因此不带上也没关系。
图17 Hm_lvt_cookie相关说明
因此,需要带上的cookie仅剩:cnonce、sig、nonce、XISESSIONID,其中XISESSIONID应该就是在cookie中存储的sessionId。nonce和XISESSIONID从URL:http://i.xiaoi.com/robot/webrobot?&callback=__webrobot__processOpenResponse&data=%7B%22type%22%3A%22open%22%7D&ts=1560763093937 通过返回协议头中的set-cookie中发现,cnonce和sign的值则是通过JS进行计算得来的。
图18
在开发人员工具的搜索栏中搜“cnonce”,只有一条结果,转到调用的地方,格式化
图19
o函数的定义就在上面,往上翻
图20
可以看到,o函数穿进去的参数分别是cookie的键和值,这样与我们的分析是一致的,cnonce的值就是t的值了。接下来分析sig的值,由图可知
sig = h(j(r) + t)
t是已知的,接下来看h函数和j函数,h函数里面层层调用,但大多都在_verify这个函数里面、
_verify: function () { var d = document; function n() { return Math.PI + 'I' } function c() { return 'no' } function h(k) { return g(f(p(k))) } function f(K) { var H = K; var I = Array(80); var G = 1732584193; var F = - 271733879; var E = - 1732584194; var D = 271733878; var C = - 1009589776; for (var z = 0; z < H.length; z += 16) { var B = G; var A = F; var y = E; var v = D; var k = C; for (var u = 0; u < 80; u++) { if (u < 16) { I[u] = H[z + u] } else { I[u] = l(I[u - 3] ^ I[u - 8] ^ I[u - 14] ^ I[u - 16], 1) } var J = q(q(l(G, 5), s(u, F, E, D)), q(q(C, I[u]), i(u))); C = D; D = E; E = l(F, 30); F = G; G = J } G = q(G, B); F = q(F, A); E = q(E, y); D = q(D, v); C = q(C, k) } return new Array(G, F, E, D, C) } function s(u, k, w, v) { if (u < 20) { return (k & w) | ((~k) & v) } if (u < 40) { return k ^ w ^ v } if (u < 60) { return (k & w) | (k & v) | (w & v) } return k ^ w ^ v } function i(k) { return (k < 20) ? 1518500249 : (k < 40) ? 1859775393 : (k < 60) ? - 1894007588 : - 899497514 } function q(k, w) { var v = (k & 65535) + (w & 65535); var u = (k >> 16) + (w >> 16) + (v >> 16); return (u << 16) | (v & 65535) } function l(k, u) { return (k << u) | (k >>> (32 - u)) } function p(v) { var k = ((v.length + 8) >> 6) + 1, w = new Array(k * 16); for (var u = 0; u < k * 16; u++) { w[u] = 0 } for (u = 0; u < v.length; u++) { w[u >> 2] |= v.charCodeAt(u) << (24 - (u & 3) * 8) } w[u >> 2] |= 128 << (24 - (u & 3) * 8); w[k * 16 - 1] = v.length * 8; return w } function g(v) { var u = '0123456789abcdef'; var w = ''; for (var k = 0; k < v.length * 4; k++) { w += u.charAt((v[k >> 2] >> ((3 - k % 4) * 8 + 4)) & 15) + u.charAt((v[k >> 2] >> ((3 - k % 4) * 8)) & 15) } return w } function e(B) { var v, u, A, w = d.cookie.split(';'); for (v = 0; v < w.length; v++) { var z = w[v]; var k = z.indexOf('='); u = z.substr(0, k); A = z.substr(k + 1); u = u.replace(/^\s+|\s+$/g, ''); if (u == B) { return unescape(A) } } } function b() { return 'n' } function o(u, k) { document.cookie = u + '=' + escape(k) + ';path=' + WebRobot.cookiePath } function a() { return 'ce' } function j(u) { var w = '', x = n().substr(0, 7); for (var v = 0; v < x.length; v++) { var y = x.charAt(v); if (y != '.') { w = y + w } } return h(u + w) } function m() { return c() + b() + a() } var r = e(m()); if (r) { var t = '' + (Math.ceil(Math.random() * 899999) + 100000); o('cnonce', t); o('sig', h(j(r) + t)) } }
然后这是我自己整理的关于计算sig的源码
function calcSig() { function n() { return Math.PI + "I" } function c() { return "no" } function h(k) { return g(f(p(k))) } function f(K) { var H = K; var I = Array(80); var G = 1732584193; var F = -271733879; var E = -1732584194; var D = 271733878; var C = -1009589776; for (var z = 0; z < H.length; z += 16) { var B = G; var A = F; var y = E; var v = D; var k = C; for (var u = 0; u < 80; u++) { if (u < 16) { I[u] = H[z + u] } else { I[u] = l(I[u - 3] ^ I[u - 8] ^ I[u - 14] ^ I[u - 16], 1) } var J = q(q(l(G, 5), s(u, F, E, D)), q(q(C, I[u]), i(u))); C = D; D = E; E = l(F, 30); F = G; G = J } G = q(G, B); F = q(F, A); E = q(E, y); D = q(D, v); C = q(C, k) } return new Array(G,F,E,D,C) } function s(u, k, w, v) { if (u < 20) { return (k & w) | ((~k) & v) } if (u < 40) { return k ^ w ^ v } if (u < 60) { return (k & w) | (k & v) | (w & v) } return k ^ w ^ v } function i(k) { return (k < 20) ? 1518500249 : (k < 40) ? 1859775393 : (k < 60) ? -1894007588 : -899497514 } function q(k, w) { var v = (k & 65535) + (w & 65535); var u = (k >> 16) + (w >> 16) + (v >> 16); return (u << 16) | (v & 65535) } function l(k, u) { return (k << u) | (k >>> (32 - u)) } function p(v) { var k = ((v.length + 8) >> 6) + 1 , w = new Array(k * 16); for (var u = 0; u < k * 16; u++) { w[u] = 0 } for (u = 0; u < v.length; u++) { w[u >> 2] |= v.charCodeAt(u) << (24 - (u & 3) * 8) } w[u >> 2] |= 128 << (24 - (u & 3) * 8); w[k * 16 - 1] = v.length * 8; return w } function g(v) { var u = "0123456789abcdef"; var w = ""; for (var k = 0; k < v.length * 4; k++) { w += u.charAt((v[k >> 2] >> ((3 - k % 4) * 8 + 4)) & 15) + u.charAt((v[k >> 2] >> ((3 - k % 4) * 8)) & 15) } return w } function e(B) { var cookie="Hm_lvt_822805145daedc4d66ed5fdf3d12cc8b=1516801663; Hm_lpvt_822805145daedc4d66ed5fdf3d12cc8b=1516801687; nonce=355117"; var v, u, A, w = cookie.split(";"); for (v = 0; v < w.length; v++) { var z = w[v]; var k = z.indexOf("="); u = z.substr(0, k); A = z.substr(k + 1); u = u.replace(/^\s+|\s+$/g, ""); if (u == B) { return unescape(A) } } } function b() { return "n" } function o(u, k) { document.cookie = u + "=" + escape(k) + ";path=" + WebRobot.cookiePath } function a() { return "ce" } function j(u) { var w = "" , x = n().substr(0, 7); for (var v = 0; v < x.length; v++) { var y = x.charAt(v); if (y != ".") { w = y + w } } return h(u + w) } function m() { return c() + b() + a() } var r = e(m()); if (r) { var t = "" + (Math.ceil(Math.random() * 899999) + 100000); var sig=h(j(r) + t); return sig } }function Cnonce(){return "" + (Math.ceil(Math.random() * 899999) + 100000)}
调用calcSig函数即可返回所计算的值
图21 计算结果
好啦,分析已经结束,要是还有不明白的或者文中所述有误的可以在下方评论,感谢各位大牛的指点。