登录百度的时候,需要的参数有点多,其余参数都千篇一律,不过前几个月多出来(我也不知道什么时候多出来的)一个 sig 跟 shaOne 参数,其实也不太清楚这两个参数是不是必须参数,不过还是研究了一下。
sig
这个 sig 参数是这样的:
SHpuaVowZG1wbENyNVlZN2Q3bzJUNHVpNUtxMWJPNHFJSDZvbEpUa1dSSXl6UTF5ajd1dmRXMnI5TldDN3FSSA==
很明显,Base64 编码,于是解码看看:
HzniZ0dmplCr5YY7d7o2T4ui5Kq1bO4qIH6olJTkWRIyzQ1yj7uvdW2r9NWC7qRH
还是 Base64 编码,那再解码看看,这次就变成“乱码”了。
差不多可以理清这样的编码顺序,密文被编码成 Base64,再编码一次 Base64。
打断点
首先打开登录页面,全局搜索 sig 关键字,最终定位在:https://wappass.baidu.com/static/waplib/moonshad.js,这个 js 里面。
再次搜索 sig 关键字,定位在这个定义 json object 的地方:
t = {
time: r.time,
alg: r[_0x19c3("0x92")],
sig: o[_0x19c3("0x9b")](r, c, _),
elapsed: (new Date)[_0x19c3("0x95")]() - n || "",
shaOne: i
}
好家伙,原来 shaOne 也在这,舒服了。
代码分析
代码是经过混淆的,不过打断点之后可以知道具体使用的方法名。
我们通过监事表达式 Watch,知道了 _0x19c3(“0x9b”) 其实是 encryption 方法。
参数
里面传入的参数分别是 r,c,_ 三个参数,c 参数不重要。
r 参数
其中,r 可以直接看到是传入了一个 json object,为:
{
"staticpage":"https://tieba.baidu.com/tb/static-common/html/pass/v3Jump.html",
"charset":"UTF-8",
"token":"da4e6e6f5420c0bf8416b3ed89bd3d8c",
"tpl":"tb",
"subpro":"",
"apiver":"v3",
"tt":1589191798699,
"codestring":"",
"safeflg":"0",
"u":"https://tieba.baidu.com/index.html",
"isPhone":"",
"detect":"1",
"gid":"C818A74-69B5-4F10-AE1C-40BF7135BD57",
"quick_user":"0",
"logintype":"dialogLogin",
"logLoginType":"pc_loginDialog",
"idc":"",
"loginmerge":"true",
"mkey":"",
"splogin":"rate",
"username":"",
"password":"",
"mem_pass":"on",
"rsakey":"",
"crypttype":12,
"ppui_logintime":23819177,
"countrycode":"",
"fp_uid":"",
"fp_info":"",
"loginversion":"v4",
"supportdv":"1",
"ds":"",
"dv":"",
"fuid":"",
"alg":"v3",
"time":1589191799
}
_ 参数
经过几次断点,发现这个 _ 参数,是一个固定值:moonshad0moonsh1,16位,应该是 AES 密钥,经过前段时间逆向极验的代码,一下就觉得可能是用这个密钥 AES 加密上面的 r 参数。
加密过程
继续跟进到 encryption 方法:
encryption: function(x, c, _) {
var t = r(x, c);
return i(n(t, _))
}
发现上面的 r 参数被当做 x 传进来了,然后通过 r 方法处理,返回了一个 t。
跟进一下 r 方法:
x.exports = function(x, c) {
var _ = [];
for (var t in x)
x.hasOwnProperty(t) && _.push(t);
_[_0x19c3("0x9c")]();
for (var r = [], n = 0, i = _.length; n < i; n++) {
var e = _[n];
r[_0x19c3("0x21")](e + "=" + x[e])
}
var o = u(r.join("&"))
, a = ""
, s = (window[_0x19c3("0x8f")][_0x19c3("0x90")] + "" + window[_0x19c3("0x8f")].height)[_0x19c3("0x9d")]("");
for (n = 0; n < s[_0x19c3("0x18")]; n++) a += h[s[n]]; return function f(x, c) { var _, t = "", r = x[_0x19c3("0x9d")](""), n = c[_0x19c3("0x9d")](""); if (r[_0x19c3("0x18")] >= n[_0x19c3("0x18")]) {
for (_ = 0; _ < n[_0x19c3("0x18")]; _++)
t += r[_] + n[_];
t += x[_0x19c3("0x9e")](_)
} else {
for (_ = 0; _ < r.length; _++)
t += r[_] + n[_];
t += c[_0x19c3("0x9e")](_)
}
return t
}(o, a)
}
这个方法没有再调用别的方法,很好翻译,整个流程就是:
先把 json object 里的 key 拿出来,放入一个列表,之后对这个列表进行字典序排列,再重新按照 key=value 这样排列放入一个列表,最后用 & 符号连接每一个值。
里面有个 u 方法,其实就是 md5 加密,所以不算外部自定义方法,将上述用 & 串好的值做 md5 加密得到一个字符串。
再通过 for 循环进行字符串重新排序组合。
使用 Python 重写如下:
def r(x):
o = md5("&".join([f"{n}={x[n]}" for n in sorted([t for t in x])]))
h = {"0":"s","1":"t","2":"r","3":"h","4":"i","5":"j","6":"k","7":"l","8":"m","9":"n","a":"3","b":"4","c":"5","d":"9","e":"8","f":"7","g":"1","h":"2","i":"6","j":"0","k":"a","l":"b","m":"c","n":"d","o":"e","p":"f","q":"g","r":"z","s":"y","t":"x","u":"w","v":"v","w":"u","x":"o","y":"p","z":"q"}
a = ""
s = ["1", "9", "2", "0", "1", "0", "8", "0"]
for n in s:
a += h[n]
return f(o, a)
def f(x, c):
t, r, n = "", list(x), list(c)
if len(r) >= len(n):
for _ in range(len(n)):
t += r[_] + n[_]
t += x[_+1:]
else:
for _ in range(len(r)):
t += r[_] + n[_]
t += c[_+1:]
return t
现在我们得到了 t 值,最终的 sig 值是 i(n(t, _))
得到的
其中 _ 值是我们上面提到的 AES 密钥,这样一看,好像确实是 AES 密钥,跟进一下 n 方法:
x[_0x19c3("0x0")] = function(x, c) {
var _ = c
, t = (c = r[_0x19c3("0x1d")].Utf8[_0x19c3("0x26")](_),
r[_0x19c3("0x1d")][_0x19c3("0x24")][_0x19c3("0x26")](x));
return r.AES[_0x19c3("0x43")](t, c, {
mode: r[_0x19c3("0x46")][_0x19c3("0x9f")],
padding: r[_0x19c3("0x4f")][_0x19c3("0x50")]
})[_0x19c3("0x14")]()
}
嗯,是 AES 加密,用的是 ECB 模式。
通过网页在线 AES 加密,把 t 参数作为需要加密的内容,_ 参数为密钥,密文用 Base64 编码,得到的结果与 n(t, _)
是一样的。
OK,再跟进一下 n 方法:
x[_0x19c3("0x0")] = function(x) {
var c, _, t, r, n, i, e = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (t = x[_0x19c3("0x18")],
_ = 0,
c = ""; _ < t; ) { if (r = 255 & x.charCodeAt(_++), _ === t) { c += e.charAt(r >> 2),
c += e[_0x19c3("0x6c")]((3 & r) << 4), c += "=="; break } if (n = x[_0x19c3("0x23")](_++), _ === t) { c += e[_0x19c3("0x6c")](r >> 2),
c += e[_0x19c3("0x6c")]((3 & r) << 4 | (240 & n) >> 4),
c += e[_0x19c3("0x6c")]((15 & n) << 2), c += "="; break } i = x[_0x19c3("0x23")](_++), c += e[_0x19c3("0x6c")](r >> 2),
c += e.charAt((3 & r) << 4 | (240 & n) >> 4),
c += e.charAt((15 & n) << 2 | (192 & i) >> 6),
c += e[_0x19c3("0x6c")](63 & i)
}
return c
}
一看到这个 e 参数,那下面不用看了,盲猜 Base64 编码。
经过测试,这个方法确实是用来做 Base64 编码的,那就跟我们上面想的一样了,AES 加密后的密文,通过两次 Base64 编码,得到了 sig。
shaOne
对,就是 SHA-1,既然知道这个值是 sha1 值,那么我们只需要不断往上层走,看看是把什么内容加密成了 sha1。
最上面可以看到,shaOne 给的是 i 参数,通过 js 再往上看,i 是通过 一个 for 循环来得到的:
var n, i = "";
for (n = i = (new Date)[_0x19c3("0x95")](); "00" !== (i = f(u(i)))[_0x19c3("0x14")]()[_0x19c3("0x20")](0, 2); )
首先给 n 和 i 赋值为 13 位时间戳,再 for 循环重新给 i 赋值并且判断如果 i 的前两位字符串等于 “00” 就跳出循环。
首先我们看看将 i 通过 u 方法执行后会得出什么结果:
>>> u(i)
<<< "e4ff899a14532fc40acc5bb651fb4190"
好像是个 md5 加密,毕竟位数都一样,先测试一波,在线将 1589191804791 用 md5 加密一下,得到了 e4ff899a14532fc40acc5bb651fb4190,简单,就是 md5 加密。省去了看代码的功夫。
那我们再看看 f(u(i))
执行后会是什么结果。
>>> f(u(i))
<<< "e7afd4765c748dcd83ba337290feec549c248d7f"
哦~ 说不定是 sha1 加密,故技重施,将 e4ff899a14532fc40acc5bb651fb4190 用 sha1 再加密一下,果然跟结果是一样的。
那就简单了,过程应该是,n 和 i 都赋值为 13 位时间戳,然后进入循环体,i 被 md5 加密 n 后再进行 sha1 加密后赋值,判断此时的 i 前两位是不是 “00”,如果是,就跳出循环,不是则继续。
按照这个逻辑写一下 Python:
import hashlib
def md5(s):
cm = hashlib.md5(s.encode('utf-8')).hexdigest()
return cm
def sha1(s):
cm = hashlib.sha1(s.encode('utf-8')).hexdigest()
return cm
def shaOne():
n = "1589191804791"
i = sha1(md5(n))
while "00" != i[:2]:
i = sha1(md5(i))
return i
运行的时候将 n 赋值为 1589191804791,运行最后得出的结果跟上面 js 运行得到的结果是一样的。
总结
sig 参数是通过将一个 json object 通过排列组合得到一个字符串,再将这个字符串 AES 加密,经过两次 Base64 编码得到。
shaOne 参数是通过将时间戳进行 md5 加密,再 sha1 加密,之后再将 sha1 做 md5 加密后再 sha1 加密,如此套娃几百次之后,得到一个 00 开头的 sha1,就是 shaOne 参数了。
现在是moonshad v3 版本了,其中 一共有5个方法,这5个方法 key 是不同的, moonshad0moonsh1 是其中一个, 这5种可能, 是根据当前时间 /86400秒 再 %5 ,取的,同1天以内应该是调用同一个方法.
百度真能玩
你好,请问,想要获得百度的token时新增了这两个参数,请问是必须的么
如果能正确返回 TOKEN,那就说明参数不是必须的呗
你好 我不知道怎么调用正在调试页面中的方法
首先你需要有一个 Google Chrome 浏览器,然后在页面按 F12,点 Source,找到你要打断点的 JS 文件,找到对应的行号,在行号上点一下,就是一个断点,具体百度~
站长有165注册账号网页吗可以不发短信的那种