源码将发布于个人公众号中,欢迎关注与投稿
文章目录
- 前言
- 1. 环境与软件
- 2. 实现效果
- 3. 分析过程
- 3.1 正常贴吧首页登录流程
- 3.2 账号框失去焦点
- 请求头分析
- 请求参数分析
- 返回值分析
- 3.3 点击提交按钮
本文为百度登录协议分析,由于协议分析常常需要试错故本文书序顺序为作者分析顺序(所以你将看到分析完某一步的时候,还要回退一步分析别之前接口)。
代码实现采用Java语言与Rhino框架实现。
java模拟部分登录接口源码
源码:
https://github.com/fanmingyi/tiebaWebProtocol
实现时间:2019年05月27日
打开Charles处理好SSL与代理相关配置,Chrome 打开开发者控制台(F12按键)观察数据请求。
当用户输入完账号时,会自然而然的把焦点移除到密码框或者其他区域。当账号输入框失去焦点时会触发账号检测接口,判断账号是否需要输入验证码。
图3.2显示账号框失去焦点时访问多个接口,这里只挑关键接口描述。其他接口并不影响整体流程
接口地址
https://passport.baidu.com/v2/api/
请求方式
GET
请求参数
请求头
无验证码
bd__cbs__5vdw2x({
"errInfo": {
"no": "0"
},
"data": {
"codeString": "",
"vcodetype": "",
"userid": "",
"mobile": "",
"displayname": "",
"isconnect": ""
},
"traceid": ""
})
有验证码(不做过多分析)
bd__cbs__61uvo4({
"errInfo": {
"no": "0"
},
"data": {
"codeString": "njG3706f5779ef5f59f029a159a5b01f98a31ee5b066a0189db",
"vcodetype": "47d9xtxhYYpw2VQmiJ7ol0uMKom+0ekdbPlIT9wX876Qrw3ZOFONQJunv5n2OB6Cw97fKMoTZIzOoG\/FHgtXH+0174AjRSgNqRUb",
"userid": "",
"mobile": "",
"displayname": "",
"isconnect": ""
},
"traceid": ""
})
从“图3.2 账号失去焦点请求头”可知此请求需带带上cookies。细看图cookies的value数值为BAIDUID,FG等。
依据WEB前端知识可知,cookies两种生成方式分别为:
当本地之前已存储cookies时,如访问同源域名将自动带上之前cookies。关于浏览器同源策略不在此做过多讲解
根据优先级我们应先分析第二种情况产生了cookies 。从正常登录流程可知登录需要打开贴吧首页,所以试探性检测贴吧首页返回的cookies信息
打开贴吧首页请求头信息如下:
从上图"图3.2 贴吧首页返回请求头"可知贴吧首页会返回一些必要的cookies用于下一步请求
采用对比法分析一些固定数值(判断固定值最好开两个不同浏览器查看结果进行比对,因为作者对于抓包有点点小经验所以一般都能看出,所以本文没有采用多开不同浏览器的策略)。
输入账号"12345"第一次模拟账号框失去焦点拦截但表单参数如下:
参数名 | 数值 |
---|---|
logincheck | |
token | a51a82e34350f43bae9007aac62fff9f |
tpl | tb |
apiver | v3 |
tt | 1557814422439 |
sub_source | leadsetpwd |
username | 12345 |
loginversion | v4 |
dv | tk0.1105670664575046915578… |
traceid | |
callback | bd__cbs__61uvo4 |
输入账号"18675969904"第一次模拟账号框失去焦点拦截但表单参数如下(手机号乱打的不是我的)
参数名 | 数值 |
---|---|
logincheck | |
token | a51a82e34350f43bae9007aac62fff9f |
tpl | tb |
apiver | v3 |
tt | 1557814395510 |
sub_source | leadsetpwd |
username | 18675969904 |
loginversion | v4 |
dv | tk0.110567066457504… |
traceid | |
callback | bd__cbs__5vdw2x |
综上对比可预估参数信息
参数名 | 说明 |
---|---|
logincheck | 空数据,应该用于区分接口 |
token | 尚不明确 |
tpl | 固定值 |
apiver | 固定值 |
tt | 时间戳 |
sub_source | 固定值 |
username | 账号 |
loginversion | 固定值 |
dv | 尚不明确 |
traceid | 空 |
callback | JSONP跨域用的,可以不传字段名与数值 |
综上所述需要分析的字段
(1)token分析
假设我们请求表单的token数值为fdc7c3bb0d7360daa1261a89fe3c487c
直接在浏览器中直接搜索fdc7c3bb0d7360daa1261a89fe3c487c看看结果如何
可以明显的看到这个token是从另一个接口而来,鼠标移动到"图3.2 token搜索结果2"中的顶部标签栏可以看到请求相关信息或者通过Charles搜搜关键字查找到对应URL
获取token接口如下:
参数名 | 数值 |
---|---|
getapi | |
tpl | tb |
apiver | v3 |
tt | 1559012718154 |
class | login |
gid | 1F8687F-6BD5-48BD-A0FB-5211E3951305 |
loginversion | v4 |
logintype | dialogLogin |
traceid | |
callback | bd__cbs__2kfado |
bd__cbs__2kfado({
"errInfo": {
"no": "0"
},
"data": {
"rememberedUserName": "",
"codeString": "",
"token": "8ed65d8c86ebecf334223c2d972cb0d6",
"cookie": "1",
"usernametype": "",
"spLogin": "rate",
"disable": "",
"loginrecord": {
'email': [],
'phone': []
}
},
"traceid": ""
})
获取token接口分析
参数名 | 数值 |
---|---|
getapi | 用于区分接口,空values |
tpl | 固定值 |
apiver | 固定值 |
tt | 时间戳 |
class | 固定值 |
gid | 尚不明确 |
loginversion | 固定值 |
logintype | 固定值 |
traceid | 空 |
callback | JSONP相关可不传 |
如法炮制在浏览器搜索gid数值,看看有没有结果。
结果如下:
从搜索结果可知gid应该js动态生成。对于此类型的参数一般需要从源码分析。直接从Chrome开发控制台中查看网络发起者源码位置,如下图
我们断点到调用栈最后一个函数如下:
根据上图分析可以得到,当调用最后一个函数时已经传入拼接好的URL地址所以包含gid,但是没有看到gid的生成代码,因此需要向上分析函数。
此处具体函数调用栈分析代码省略部分,我们直接看最终向上寻找gid生成代码处。
“图3.2 gid源码分析1”发了重要gid代码提取处,但不清楚这个gid是在文件哪个部分生成。所以复制“图3.2 gid源码分析1”源码到WebStorm中分析得到后两张图结果。
致辞gid生成算法分析完成
function generatedGid() {
return "xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(e) {
var t = 16 * Math.random() | 0
, n = "x" === e ? t : 3 & t | 8;
return n.toString(16)
}).toUpperCase()
}
gid分析完成后token数值也就清晰可见。
最后还差一个参数尚未分析dv。
(2)dv分析
经过前面的分析估计你已经忘记dv是啥了。dv是账号框失去焦点时进行账号检测所需递交的参数之一。
根据前文的介绍相信对作者的分析方式已经有所了解,总结来说就是:
(1)对比多次数据判断数据是否是固定值。(记得删除cookies和更换浏览器进行比对)如果是固定值那么不做分析
(2)直接在Chrome搜索对应values,此处可以判断参数是否通过jsonp等方式获取,或者直接写死在某一个html或js中
(3)查看cookies是否存在对应参数 (本文没有这么做,因为大厂很少把cookies取出在放入参数中)
(4)Chrome查看网络发起源,根据调用栈分析,如果发现重要关键步骤,复制js源码到WebStorm进行交叉引用分析
这处直接给出dv生成算法
/*
* 加密算法
* */
initEncyptArray.prototype = {
'int': function (e) {
return encryptInt(e, this.dict)
},
iary: function (mArray) {
for (var t = '', n = 0; n < mArray.length; n++) {
var r = encryptInt(mArray[n], this.dict2);
t += r.length > 1 ? r.length - 2 + r : r
}
return t
},
bary: function (mArray) {
for (var t = 0, n = {}, r = 0; r < mArray.length; r++)
mArray[r] > t && (t = mArray[r],
n[mArray[r]] = !0);
var o = parseInt(t / 6);
o += t % 6 ? 1 : 0;
for (var a = '', r = 0; o > r; r++) {
for (var i = 6 * r, d = 0, c = 0; 6 > c; c++)
n[i] && (d += Math.pow(2, c)),
i++;
a += this.dict[d]
}
return a
},
str: function (e) {
for (var t = [], n = 0; n < e.length; n++) {
var r = e.charCodeAt(n);
r >= 1 && 127 >= r ? t.push(r) : r > 2047 ? (t.push(224 | r >> 12 & 15),
t.push(128 | r >> 6 & 63),
t.push(128 | r >> 0 & 63)) : (t.push(192 | r >> 6 & 31),
t.push(128 | r >> 0 & 63))
}
for (var o = '', n = 0, a = t.length; a > n;) {
var i = t[n++];
if (n >= a) {
o += this.dict[i >> 2],
o += this.dict[(3 & i) << 4],
o += '__';
break
}
var d = t[n++];
if (n >= a) {
o += this.dict[i >> 2],
o += this.dict[(3 & i) << 4 | (240 & d) >> 4],
o += this.dict[(15 & d) << 2],
o += '_';
break
}
var c = t[n++];
o += this.dict[i >> 2],
o += this.dict[(3 & i) << 4 | (240 & d) >> 4],
o += this.dict[(15 & d) << 2 | (192 & c) >> 6],
o += this.dict[63 & c]
}
return o
}
}
/**
* 初始化一个加密数组
* @param token
*/
function initEncyptArray(token) {
var encyptArray = [[48, 57], [65, 90], [97, 122], [45, 45], [126, 126]]
, dict = expandArray(encyptArray)
, dict2 = expandArray(encyptArray.slice(1));
token && (dict = funReoder(dict, token),
dict2 = funReoder(dict2, token)),
this.dict = dict,
this.dict2 = dict2
}
/**
* 重排序encryptArray
* @param encryptArray
* @param token
* @returns {*}
*/
function funReoder(encryptArray, token) {
for (var tokenArray = token.split(''), r = 0; r < encryptArray.length; r++) {
var encryptArrayIndex = r % tokenArray.length;
encryptArrayIndex = tokenArray[encryptArrayIndex].charCodeAt(0),
encryptArrayIndex %= encryptArray.length;
var encryptArrayChar = encryptArray[r];
encryptArray[r] = encryptArray[encryptArrayIndex],
encryptArray[encryptArrayIndex] = encryptArrayChar
}
return encryptArray
}
/**
* 展开一个二维数组
* @param mExpandArray
* @returns {Array}
*/
function expandArray(mExpandArray) {
for (var arrayRel = [], n = 0; n < mExpandArray.length; n++)
for (var r = mExpandArray[n][0]; r <= mExpandArray[n][1]; r++)
arrayRel.push(String.fromCharCode(r));
return arrayRel
}
/**
* 转化某个int类型字符串
* @param encyptIntStr
* @param encryptDictitonArray
* @returns {*}
*/
function encryptInt(encyptIntStr, encryptDictitonArray) {
var rel = '';
var encyptInt = Math.abs(parseInt(encyptIntStr));
if (encyptInt) {
for (; encyptInt;) {
rel += encryptDictitonArray[encyptInt % encryptDictitonArray.length],
encyptInt = parseInt(encyptInt / encryptDictitonArray.length);
}
} else {
rel = encryptDictitonArray[0];
}
return rel
}
/**
* 用加密算法拼接信息
* @param info
* @param token
* @returns {string}
*/
function joinBaseBrowserMsg(info, token) {
var encyptObj = new initEncyptArray(token);
var broserInfo = {
flashInfo: 0,
mouseDown: 1,
keyDown: 2,
mouseMove: 3,
version: 4,
loadTime: 5,
browserInfo: 6,
token: 7,
location: 8,
screenInfo: 9
};
var resultArray = [encyptObj.iary([2])];
for (var infoProperty in info) {
var infoPropertyEntity = info[infoProperty];
if (void 0 !== infoPropertyEntity && void 0 !== broserInfo[infoProperty]) {
var temp;
if ('number' == typeof infoPropertyEntity) {
(temp = infoPropertyEntity >= 0 ? 1 : 2, infoPropertyEntity = encyptObj.int(infoPropertyEntity));
} else {
if ('boolean' == typeof infoPropertyEntity) {
(temp = 3, infoPropertyEntity = encyptObj.int(infoPropertyEntity ? 1 : 0))
} else {
if ('object' == typeof infoPropertyEntity && infoPropertyEntity instanceof Array) {
(temp = 4, infoPropertyEntity = encyptObj.bary(infoPropertyEntity))
} else {
(temp = 0, infoPropertyEntity = encyptObj.str(infoPropertyEntity + ''))
}
}
}
infoPropertyEntity && resultArray.push(encyptObj.iary([broserInfo[infoProperty], temp, infoPropertyEntity.length]) + infoPropertyEntity);
}
}
return resultArray.join('')
}
var token = 'tk' + Math.random() + (new Date).getTime()
var info = {
"mouseDown": "",
"keyDown": "",
"mouseMove": "",
"version": 26,
"loadTime": (new Date()).valueOf(),
"browserInfo": "3,2,74",
"token": token,
"location": ",undefined",
"screenInfo": "23,0,1904,44,1920,1080,1920,1920,992"
}
var result = token + '@' + joinBaseBrowserMsg(info, token)
上诉result便是dv生成结果
bd__cbs__5vdw2x({
"errInfo": {
"no": "0"
},
"data": {
"codeString": "",
"vcodetype": "",
"userid": "",
"mobile": "",
"displayname": "",
"isconnect": ""
},
"traceid": ""
})
有验证码(不做过多分析)
bd__cbs__61uvo4({
"errInfo": {
"no": "0"
},
"data": {
"codeString": "njG3706f5779ef5f59f029a159a5b01f98a31ee5b066a0189db",
"vcodetype": "47d9xtxhYYpw2VQmiJ7ol0uMKom+0ekdbPlIT9wX876Qrw3ZOFONQJunv5n2OB6Cw97fKMoTZIzOoG\/FHgtXH+0174AjRSgNqRUb",
"userid": "",
"mobile": "",
"displayname": "",
"isconnect": ""
},
"traceid": ""
})
如果是有验证码类型的后面自己再简单分析下 接入打码平台即可。关于验证码作者会出东方财富的协议分析讲解打码。
点击登录后触发四个协议。
后文请求接口的cookies返回和提交不再做过多讲解,一句话就是把以前的接口返回的cookies带上,和把新请求的cookies保存合并。
(1)获取RSA密钥接口分析
URL:
https://passport.baidu.com/v2/getpublickey
请求方式:
GET
请求参数:
参数名 | 数值 |
---|---|
token | 上文有讲解token 的获取 |
tpl | 固定值 |
apiver | 固定值 |
tt | 时间戳 |
gid | 上文有讲解 |
loginversion | 固定值 |
traceid | js计算生成 |
callback | JSONP相关可不传 |
返回值
{
"errno":'0',
"msg":'',
"pubkey":'-----BEGIN PUBLIC KEY----- \nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1m+0pgXdwA3MXL+21PsURrNYC\nJgwEMqx1Fib52dGSh3yMjwAvFgGdC7AqmF3LElYuA8eRCIwlwzQ3RIAnWd+4uihj\nd07Iz5d4NMaCbkjQFhTpYQDRzfJHaIam9kaCwXeIP32hy6JXvAJmYrowy5ZbvTAF\n1tf\/VCJjqO7TUkkAFQIDAQAB\n-----END PUBLIC KEY-----\n',
"key":'KcoJV7Cr4Btbig6aaDJa0YfEAb72dp2F',
"traceid": "16F55C01"
}
示例:
https://passport.baidu.com/v2/getpublickey?token=6dd25c0c14c43f75f8b4d5225064b213&tpl=tb&apiver=v3&tt=1559136918602&gid=7131BB0-D111-439D-9C75-9CD2EB855501&loginversion=v4&traceid=16F55C01&callback=bd__cbs__9umh06
上面就差traceId我们不知道怎么生成了,此处直接给出算法
function createHeadID() {
var t = (new Date).getTime() + getRandom().toString()
, n = Number(t).toString(16)
, i = n.length
, s = n.slice(i - 6, i).toUpperCase();
return s;
}
function getRandom() {
return parseInt(90 * Math.random() + 10, 10)
}
createHeadID()+"01"
(2)提交用户轨迹到服务器
URL:
https://passport.baidu.com/viewlog
请求方式:
GET
请求参数:
参数名 | 数值 |
---|---|
ak | 1e3f2dd1c81f2075171a547893391274(定值) |
as | 2283e880(定值) |
fs | 计算生成 |
v | 随机数 |
callback | JSONP相关可不传 |
返回值
{"code":0,
"data":{
"tk":"6571L4PexHC2mg5aMjl2hgiywV8fkmyJhToo9rGuiiswaRjimqr5bAc9eDG8ss\/ltjVr",
"as":"2283e880",
"ds":"iiDK6LRWXfVqYSyyx1Cw2cgDrtgEXlE\/ZUC3c9+t6umTjBOy4Za9kO+BctvtqpLnn3Okj0lrS70L5KO5Cm8dG62GZpoKBBqLMx8N9EOpQjarQ\/6FMJOjoW8ma1wAClGQGMiv2wb6e\/nwIPoxuXZmTaRMcheTXTA1MUmZ4KuO89Ebjuk5n\/S2LoNPqq9t\/eMgn\/4rDHLeoTQFJqwBs2LlioZzTD6cAJcHSmVtOoLt1cJkz08FmmXaK3z\/ZI+TxwLEpL7PuoIOLQbZ05F0Nj8FmzE98vFam4Vv2Js1opuXjUgIwBaGUbpSEf4I8zYKjuT1BfK5f9VVwm3\/hdGSfdDT8WvTK5a\/NVleIxydSejZCLi8i1YhYUU7AkOtlSJQFOgMui5MNC+Zuy68vKVPURnuPlR82F5H+DNMvxETRK2VLBs7TGqo2OVf1k5ReaVC+kcr8EileT1BnWRNoq9LZ6\/waRtJ5Ew03XVoYJXnI1X1m\/ei8Gk4gDm\/0655Z7s3QGrdF07cI8fMUmj9Z5j2iCH6UldSKvewmqtAoCNFlHp3yFEfa7YAftqS1Ub5skqK0CrMvc0it0ztJtz1x+7R1\/F6vMrury6jzyHAf1Arp4uquKiqV85llaAbFYDJg\/IvYAtE0zPfYCFPAmoTlKvqXOzKnzyFSeRmMLGp+fEoGYhTmEVzlU\/BFT81V\/4sdIexkhHvdlKDzcc6kK65T5ZBGa4xsnY++4sZHTtGHooyf1fBu+gH1j9\/0c59Vczg8Lo6is2PrUXnM4e6f1dMgDeL9Dls3VrxFI6nKrb8G1bUPNSfQWre+HT63X31TZ1Z9S7soxx9JhXobtgM\/bqnBFinhtqpbDHXEWGkVSKR5bKuiXgwRGREs2m+il5yFbuIip9M3LYUnryU240427CSYurNSz3EKB5ncLXTgM++loRdTKMZtzJC8nYFhogldtRuaLY34wJvAo66IhCiaKLIohP3oHf32dTHVvZ3LHDjIfE6TNTAiq8FQYDSJT6xBqs6B0RyDf94o0WBkrqoyqt7yf\/lAY\/ALRX7N94x9uByTM2BLhnYByXMTIrC5co1JMgOPLkgooJt2uxtkzGZ9Michi0EWtYCNe+zOg9vwO4EHXwuUi7fhcHGHy5BD7EnQcTn+KoSzDuxV2aSVWUVglXPiEGobrKa0tssYm6Hbk8IuBrN8GgUg8frfWtFK1LCGzsWr78z7C+etARXfmxYo4XC8ri0myBTdCuoVspZ5h1UHvn5sgdS0tPB6lYh+zzCjVpTb5TU90le3Rlzr5ccj1p8Kn8GHYi24\/ZMTjl9IcuypsZZ3tIIpQadVtOePs6U2nTmyGeDbDEtgIZYiIxtjYQPhcEBzNGZls6OQ9uDDM1BgoIh9WUSv0M="
}
示例:
https://passport.baidu.com/viewlog?ak=1e3f2dd1c81f2075171a547893391274&as=2283e880&fs=6Y20xog7DXa710pcCvK08tNqzM5XIxpA6moW1MmZ8oEpZ38VgWP0YfqaJUUQcK4qjh%2F%2BpZbJGkZQfdpJjvRGsL1eNis1pE%2FCkbwu9eEs5NDV5P1gwRKQq7ruEhJrtbIwZuQY5Ypda6ZFVyeZV3ceW%2Fk7ZEUwcEEGpl3zTk3R31Wg7wrAdu6hLLH9eFwH21BDM25%2Fls2dkBWUOqsnKiYnTMWlIniPxw9ONginb%2FjH495SqFNYyU4%2FGZIXquA%2BNEa2ccUdsSFub1fSnptNdHDsBEcEo9BHj90J9uJQXDUp5NeTNhq0agSfiD8%2BZ1%2BWw2KayaqMyKf5t8g4CmDzh0WhCAEmGc%2FZGXhHFwYc8aTsqeMKFGnkgU04DRAE5uwDxppjeXN5n5OJvR1KMh9GDezRxwzQxAPk9j1wyYZQW9tWPK6IgBsAYBiAvNjPakPkSsNsEE184CoS9YoKZ3yy4xwd6ozh5nQ5VXKa38Mbn5tFiNxznWiLICYRgD80853aZNQoay36zRIOwxTPIuumwhRrKIwme%2F2xFyZrvAwBOGRGT8LxZ0UMTtgTYBuTLufhLsDMN8DQnEm1Sz75HHx0q78zRxQMJ%2FKJ9fX4R6urvykOmM4TLGgCI%2B7zASxeIId%2Fn9YA6G2mmQQd6MEYO4dFXL8X0eBtmeSSehYEL%2Fwb0eDe5Jd6NxzP8OldCl22TpDn%2Bjdz6xzK3JmlSfMRT4yqJq468iN%2BAL50ULsTJoWsEU0IZGLwi1SeB7ZzSt7mfBiZ9KD3%2B2alnJp5kHqvO89XvRRHL3YUIqimGQXmGl86qN7Gg%2BXd4r5iclJTZdCvmH5G%2Fyb7v93PG6iEsSFfmSTxDF6Q%2BobP5jcmitDFcZUdIjFfjBzyMt858IHa1ERu5grQwPDp%2BnQMiU6vAVuhsMrLLfmWcIUgXJFSyZ%2BdBzldZYd6xgEFYx%2F%2FVCvmnTMVTOrN60v0OzEJsTgf%2FAB3rLUsWRp8DapAEXGVOfyTYRVyhfQ7uaOFov%2BNDx6F53Rpx6h63TyUNjFC%2FUmashDvMO1wLku%2F6c0bE1i80yhpiYBDZko7pM%2B4VSp7G0LxHFaZq8dIvswTrQ0CE6A%2BY7D7w%2B7YhXEliFObLgrLWVXDinQd6Erj1seGcYc5PVlb2fISu%2FdR%2BrRriPYhUROU1pgj4DIwwL%2F9TU3JvOHmPg6ug0MEn%2FIKn0xTTnQt66Q9cR0vSl%2Bs4d0zUY7G%2FHAVoHeQiiXlurtNqqvcLaB29laqUQQnYdvRicHQg%2Bv95q8a%2BDn%2F0AqmWlgHNqLr6QwsLYA4ibcpv8v9Z%2Fg9Rng7x7MbClvesLLpagdwM3L6Q0Sis%2BN4izM6REB%2BIoVfhMjfxg2HSI89Cg91SQ%3D%3D&callback=jsonpCallbackb5057&v=1996
fs参数算法:
已去除windows,docment函数,可以直接使用js生成fs(Java可以使用Rhino框架,Python使用Pyv8)
fs参数算法js源码
https://github.com/fanmingyi/tiebaWebProtocol/blob/master/src/main/resources/baiduEncyptFS.js
请自信查看上诉最后几行即可,本作者使用Rhino 框架运行js所以注释了var encypteText="" (Rhino支持java层声明一个js对象)。
encypteText对象是什么?encypteText是一个json字符串包含用户的一些点击和鼠标移动轨迹
//当前时间戳
var time = System.currentTimeMillis()
//将上面时间戳带入字符串并少许修改时间。比如:${time - 552}就是讲当前时间戳加上500毫秒返回在凭借字符串,可自行更换你所熟系的语言生成。此处为kotkin语言
var msg =
"""{"cl":[{"x":883,"y":150,"t":${time}},{"x":918,"y":234,"t":${time + 539}},{"x":898,"y":63,"t":${time + 1508}},{"x":904,"y":30,"t":${time + 1872}}],"mv":[{"fx":1008,"fy":20,"t":${time - 1293}},{"fx":946,"fy":246,"t":${time - 552}},{"fx":962,"fy":194,"t":${time - 395}},{"fx":915,"fy":151,"t":${time - 238}},{"fx":883,"fy":150,"t":${time + 160}},{"fx":916,"fy":225,"t":${time + 318}},{"fx":918,"fy":235,"t":${time + 911}},{"fx":897,"fy":250,"t":${time + 1067}},{"fx":901,"fy":128,"t":${time + 1225}},{"fx":898,"fy":63,"t":${time + 1690}},{"fx":904,"fy":31,"t":${time + 2095}},{"fx":906,"fy":46,"t":${time + 2267}},{"fx":909,"fy":59,"t":${time + 2424}},{"fx":914,"fy":71,"t":${time + 2582}},{"fx":914,"fy":76,"t":${time + 3175}},{"fx":917,"fy":164,"t":${time + 3332}}],"sc":[],"kb":[{"key":"a","t":${time + 2285}}],"cr":{"screenTop":23,"screenLeft":0,"clientWidth":1905,"clientHeight":257,"screenWidth":1920,"screenHeight":1080,"availWidth":1920,"availHeight":1008,"outerWidth":1920,"outerHeight":991,"scrollWidth":1905,"scrollHeight":1905},"ac_c":0}"""
v参数生成算法比较简单,具体如下
Math.floor(1e4 * Math.random() + 500).toString()
(3)提交密码
URL:
参数:
参数 | 数值 |
---|---|
staticpage | https://tieba.baidu.com/tb/static-common/html/pass/v3Jump.html(固定) |
charset | UTF-8 (固定) |
token | 上文有所讲述 |
tpl | tb(规定值) |
subpro | |
apiver | v3(固定) |
tt | 时间戳 |
codestring | 验证码(此处不做分析,如果没有验证码就为空) |
safeflg | 0 (固定) |
u | https://tieba.baidu.com/ (固定) |
isPhone | |
detect | 1 (固定) |
gid | 上文已讲解 |
quick_user | 0 (固定) |
logintype | dialogLogin (固定) |
logLoginType | pc_loginDialog (固定) |
idc | |
loginmerge | true(固定) |
splogin | rate(固定) |
username | 账号 |
password | 使用公钥加密后的密码 |
mem_pass | on (固定) |
rsakey | 获取公钥接口包含此参数 |
crypttype | 12(固定) |
ppui_logintime | 429817(随机数你可以写死) |
countrycode | |
fp_uid | |
fp_info | |
loginversion | v4(固定) |
ds | 上传用户轨迹时返回json 有 |
tk | 上传用户轨迹时返回json有 |
dv | 上文已有 |
traceid | 上文已有 |
返回值:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
head>
<body>
<script type="text/javascript">
var href = decodeURIComponent("https:\/\/tieba.baidu.com\/tb\/static-common\/html\/pass\/v3Jump.html")+"?"
var accounts = '&accounts='
href += "err_no=4&callback=parent.bd__pcbs__ywe98r&codeString=&userName=caizhangtao99209%40163.com&phoneNumber=&mail=&hao123Param=&u=https://tieba.baidu.com/&tpl=tb&secstate=&gotourl=&authtoken=&loginproxy=&resetpwd=&vcodetype=&lstr=<oken=&bckv=&bcsync=&bcchecksum=&code=&bdToken=&realnameswitch=&setpwdswitch=&bctime=&bdstoken=&authsid=&jumpset=&appealurl=&realnameverifyemail=0&traceid=4C5E3C01&realnameauthsid=&bind_mobile_token=&upgrade_mobile_token=&upgrade_mobile=&guide_upgrade_mobile=&scscene=&scnewuser="+accounts;
if(window.location){
window.location.replace(href);
}else{
document.location.replace(href);
}
script>
body>
html>
仔细看返回的html中的js链接err_no=4,便是登录结果错误码为4,部分请求错误码如下,如果账号密码返回正确保存cookies就可以去玩了(可能存在邮箱验证 请自行后续分析)
完整版请参考https://ss0.bdstatic.com/5LMZfyabBhJ3otebn9fN2DJv/passApi/js/loginv4_21b4899.js
"-1": {
// msg: '系统错误,请您稍后再试,帮助中心',
// field: ""
// },
// 1: {
// msg: "您输入的帐号格式不正确",
// field: "userName"
// },
// 2: {
// msg: '用户名或密码有误,请重新输入或找回密码',
// field: "userName"
// },
// 3: {
// msg: "验证码不存在或已过期,请重新输入",
// field: ""
// },
// 4: {
// msg: "帐号或密码错误,请重新输入或者找回密码",
// field: "password"
// },
// 5: {
// msg: "",
// field: ""
// },
// 6: {
// msg: "您输入的验证码有误",
// field: "verifyCode"
// },
// 7: {
// msg: "用户名或密码有误,请重新输入或找回密码",
// field: "password"
// },
// 16: {
// msg: '您的帐号因安全问题已被限制登录,帮助中心',
// field: ""
// },
// 257: {
// msg: "请输入验证码",
// field: "verifyCode"
// },
// 100027: {
// msg: "百度正在进行系统升级,暂时不能提供服务,敬请谅解",
// field: ""
// },
// 120016: {
// msg: "",
// field: ""
// },
// 18: {
// msg: "",
// field: ""
// },
// 19: {
// msg: "",
// field: ""
// },
// 20: {
// msg: "",
// field: ""
// },
// 21: {
// msg: "没有登录权限",
// field: ""
// },
// 22: {
// msg: "",
// field: ""
// },
// 23: {
// msg: "",
// field: ""
// },
// 24: {
// msg: "百度正在进行系统升级,暂时不能提供服务,敬请谅解",
// field: ""
// },
// 400031: {
// msg: "请在弹出的窗口操作,或重新登录",
// field: ""
// },
// 400032: {
// msg: "",
// field: ""
// },
// 400034: {
// msg: "",
// field: ""
// },
// 401007: {
// msg: "您的手机号关联了其他帐号,请选择登录",
// field: ""
// },
// 120021: {
// msg: "登录失败,请在弹出的窗口操作,或重新登录",
// field: ""
// },
// 500010: {
// msg: "登录过于频繁,请24小时后再试",
// field: ""
// },
// 200010: {
// msg: "验证码不存在或已过期",
// field: ""
// },
// 100005: {
// msg: "系统错误,请您稍后再试",
// field: ""
// },
// 120019: {
// msg: "请在弹出的窗口操作,或重新登录",
// field: "userName"
// },
// 110024: {
// msg: "此帐号暂未激活,重发验证邮件",
// field: ""
// },
// 100023: {
// msg: "开启Cookie之后才能登录,如何开启?",
// field: ""
// },
// 17: {
// msg: '您的帐号已锁定,请解锁后登录',
// field: "userName"
// },
// 400401: {
// msg: "",
// field: ""
// },
// 400037: {
// msg: "",
// field: ""
// },
// 50023: {
// msg: "1个手机号30日内最多换绑3个账号",
// field: ""
// },
// 50024: {
// msg: "注册过于频繁,请稍候再试",
// field: ""
// },
// 50025: {
// msg: "注册过于频繁,请稍候再试;也可以通过上行短信的方式进行注册",
// field: ""
// },
// 50028: {
// msg: '帐号或密码多次输错,请3个小时之后再试或找回密码',
// field: ""
// },
// 50029: {
// msg: '帐号或密码多次输错,请3个小时之后再试或找回密码',
// field: ""
// },
// 50030: {
// msg: "抱歉,该手机号的申请次数已达当日上限,请更换手机号",
// field: ""
// },
// 50031: {
// msg: "抱歉,该手机号的申请次数已达当月上限,请更换手机号",
// field: ""
// },
// 50032: {
// msg: "抱歉,该手机号的申请次数已达本季度上限,请更换手机号",
// field: ""
// },
// 400413: {
// msg: "",
// field: ""
// },
// 400414: {
// msg: "",
// field: ""
// },
// 400415: {
// msg: "帐号存在风险,为了您的帐号安全,请到百度钱包/理财/地图任一APP登录并完成验证,谢谢",
// field: ""
// },
// 400500: {
// msg: "您登录的帐号已注销,请登录其他帐号或重新注册",
// field: ""
// },
// 400702: {
// msg: "该手机号码登录功能已关闭,请通过用户名和密码登录",
// field: "userName"
// },
// 72200: {
// msg: "您的帐号因冻结暂时无法登录,请前往冻结时的手机APP,在登录页点击遇到问题进行解冻",
// field: ""
// },
// 96001: {
// msg: "您的帐号因违反百度用户协议被限制登录",
// field: ""
// },
// 400703: {
// msg: "手机号已被运营商二次放号,请前往https://passport.baidu.com/v2/?reg注册新帐号",
// field: ""
// }
// },
// t.errMsg.checkVerifycode = {
// 500002: {
// msg: "您输入的验证码有误",
// field: "verifyCode"
// },
// 500018: {
// msg: "验证码已失效,请重试",
// field: "verifyCode"
// }
// }
首先利用获取的公钥进行加密,而后对其base64加密。
此处你可以用其他语言实现或者使用如下js
你可以利用如下的js进行完成
https://github.com/fanmingyi/tiebaWebProtocol/blob/master/src/main/resources/baiduLoginPwdEncrypt.js
百度核心登录算法分析完毕。