经测试,cookie中需携带gid和timestamp2。参数整理有点乱,仅供参考。
xhsFingerprintV3,VERSION: '2.1.2'
流程概述
初次无cookie访问,先由 fe_api/burdock/v2/shield/registerCanvas?p=cc 接口返回 timestamp2。
gid 和 gid.sign 参数由 /fe_api/burdock/v2/shield/profile 接口返回。
此时的gid未经过验证校验,不可使用。
然后由 /ca/v1/register 接口注册验证码。
返回rid和验证码信息。
接着由 /ca/v2/fverify 接口提交验证信息。
验证后携带 cookie和 deviceId 信息,由 /fe_api/burdock/v2/shield/captcha?c=pp 接口认证设备和cookie信息。
验证成功后,携带gid和timestamp2可正常访问。
或者也可以先注册验证码,完成/v2/fverify的校验;
然后注册gid,再根据gid生成timestamp2。
最后完成 /v2/shield/captcha 验证。
timestamp2
timestamp2和上个版本相同,不重复写了。
xhr定位即可。
sign = l("G89CfW4k", t,base64_t);
id = md5 ( sign + "RRq9y03tuV")
滑块验证参数
Params
_0x225e91, _0xffa132 分别是DES加密时用到的 string 和 key
可复制到在线加密工具中测试,string为0.05,key为8bf2ae7b,加密结果和浏览器返回结果相同。
DES加密算法,模式ECB,填充 ZeroPadding,无iv。
轨迹FN
移动距离 253 。
轨迹参数用Des解密后,可发现最后一组数是拖动的距离。
captcha deviceId
最后 shield/captcha?c=pp 验证时需要用到 deviceId 。
SMSdk.getDeviceId()
getDeviceId()方法
可见_0x622d0e 还未加密,
在执行_0x645c91[_0x36bd7e(0x1c0)](_0x54a23b, _0x301c7f, _0x5deb6b);之后,返回了加密的 deviceId
ProfileData
shield/profile 注册gid时要用到profileData。
定位调试。
'{"x1":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36","x2":false,"x3":"zh-CN","x4":24,"x5":8,"x6":1,"x8":20,"x9":"1920;1080","x10":"1920;1040","x11":-480,"x12":"Asia/Shanghai","x13":true,"x14":true,"x15":true,"x16":false,"x17":true,"x18":"unknown","x19":"Win32","x20":"unknown","x21":"PDF Viewer,Portable Document Format,application/pdf,pdf,text/pdf,pdf;Chrome PDF Viewer,Portable Document Format,application/pdf,pdf,text/pdf,pdf;Chromium PDF Viewer,Portable Document Format,application/pdf,pdf,text/pdf,pdf;Microsoft Edge PDF Viewer,Portable Document Format,application/pdf,pdf,text/pdf,pdf;WebKit built-in PDF,Portable Document Format,application/pdf,pdf,text/pdf,pdf","x22":"10cfbbb02b2606dbc2ccb15a3cd2b558","x23":false,"x24":false,"x25":false,"x26":false,"x27":false,"x28":"0;false;false","x29":"4;7;8","x30":"swf object not loaded","x31":"124.04347527516074","x32":"id=;gid=a0034395f52573a18eca1fa7063d8fdcfc49bdd8deeb6a38a3db74db970fb504;audioinput;;id=;gid=a0034395f52573a18eca1fa7063d8fdcfc49bdd8deeb6a38a3db74db970fb504;audiooutput;","x33":0,"x34":0,"x35":0,"x36":1,"x37":"0|0|0|0|0|0|0|0|0","x38":"0|0|0|0|1|0|0|0|0|0"}'
代码比较简单,扣下来调用就行。
但是注意下提交的参数,x22是canvas,要和注册timestamp2的一致,x32是动态值。
x32的gid是指webRTC中本地媒体MediaDevice的groupId,和cookie中的gid不同。
查看chrome的enumerateDevices:
navigator.mediaDevices.enumerateDevices()
.then((list) => {
console.log(list);
});
这个Id每次访问会变更,可以自己随机生成一串。
所在设备在被限制时,可更换canvasId和enumerateDevices的Id。
x-s-common
x-s-common用的地方比较多,注册和校验都需要。
x-s-common定位。
其他参数如下。
x5: Dt.a.get("a1"),
x8: localStorage.getItem("b1"),
x9: fn(P()(t = P()(n = "".concat(r)).call(n, o)).call(t, localStorage.getItem("b1")))
x5生成
x5依赖cookie的a1参数
document = {}
document.cookie = 'smidV2=202210181839545813c4484407e8fd7dec32a81165dfce00c99e85a19023ff0; a1=1843b69be841gkjrtg498n4cr8tw29sxm8l4mk2cs00000809560; gid=yY4qDKjDSq68yY4qDKjDdYTJY4ykTVF9k4jY1SMI4SFY9488EJjMC6888Y8j2K88JfjSJK2y'
n = {
read: function(e) {
return '"' === e[0] && (e = e.slice(1, -1)),
e.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent)
},
write: function(e) {
return encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g, decodeURIComponent)
}
}
function x5_get(e) {
if ("undefined" != typeof document && (!arguments.length || e)) {
for (var t = document.cookie ? document.cookie.split("; ") : [], r = {}, o = 0; o < t.length; o++) {
var i = t[o].split("=")
, a = i.slice(1).join("=");
try {
var s = decodeURIComponent(i[0]);
if (r[s] = n.read(a, s),
e === s)
break
} catch (c) {
console.log(c)
}
}
return e ? r[e] : r
}
}
console.log("x5:",x5_get("a1"))
x8生成
定值 localStorage.getItem("b1")
x9生成
x9简化之后就是 fn(localStorage.getItem("b1"))
主要生成代码如下:(部分太长的代码段没贴,如tn、cn、rn、en)
var fn = function(e) {
for (var t, n, r = 240, o = 253, i = 258, a = 197, s = 264, c = 230, u = 209, l = 262, f = 237, p = 261, d = en, h = {
zDTTc: function(e, t) {
return e === t
},
nrqZm: d(251),
MyaJd: function(e, t) {
return e < t
},
fqoGO: function(e, t) {
return e ^ t
},
qcRcu: function(e, t) {
return e ^ t
},
yhYKD: function(e, t) {
return e & t
},
qxGVA: function(e, t) {
return e >>> t
},
jTaFf: function(e, t) {
return e ^ t
},
kQlol: function(e, t) {
return e >>> t
}
}, v = 256, m = []; v--; m[v] = t >>> 0)
for (n = 8,
t = v; n--; )
t = 1 & t ? 3988292384 ^ h['kQlol'](t, 1) : h['kQlol'](t, 1);
return function(e) {
var t = d;
if (h['zDTTc']('string', 'string')) {
for (var n = 0, r = -1; h['MyaJd'](n, e['length']); ++n)
r = h['fqoGO'](m[h['qcRcu'](h['yhYKD'](r, 255), e['charCod' + 'eAt'](n))], h['qxGVA'](r, 8));
return h['qcRcu'](h['jTaFf'](r, -1), 3988292384)
}
for (n = 0,
r = -1; n < e['length']; ++n)
r = m[h['jTaFf'](255 & r, e[n])] ^ h['qxGVA'](r, 8);
return -1 ^ r ^ 3988292384
}
}()
var x9 = fn("8utvIv7eYlPiIvcmIk0sxn5e6D3s0PwfQ0MAHZFRICH0+VthIhDaNom3nqt9aPwjI3LdKU7e3BkLmbPH+LENaqwMIkveTb5sVBAefVt18WukpqtVeVt4muw4IC6sx7YEIE3e0/Kekf6e0WPP87MfPMdsxVw/IhWNICu=")
console.log("x9:",x9)
smidV2
hook cookie。hook代码可到 工具站 查找。
定位分析,
在注册设备id过程中,_0x622d0e时生成了smidV2
这样看,deviceId 和x-s-common参数有关联关系。
a1
生成 x-s-common 时需要a1,注册sig时也需要。
a1用hook插件hook。
a1 = M
M = P()(S = "".concat(I)).call(S, N).substring(0, 52)
格式化一下
var j, A, k, C, S, R = 0, E = "000",
I = P()(j = P()(A = P()(k = P()(C = "".concat((+new Date).toString(16))).call(C, zt(30))).call(k, R)).call(A, 0)).call(j, E),
N = sn(I),
M = P()(S = "".concat(I)).call(S, N).substring(0, 52);
代码段翻译一下,先生成I,I = "".concat((+new Date).toString(16)) + 随机30个字符 + 00000
再生成N, N = sn(I)
最后M = (I +N).substring(0, 52)
a1生成代码
function rn() {
var e = ["bdXdq", "length", "susu", "zDTTc", "q42KWYj", "xLgpd", "0DSfdik", "nFsLu", "30jemUol", "fHjkg", "saFEz", "hECvuRX", "HjBDl", "yhYKD", "x3VT16I", "EYvYO", "pKSXB", "bnEJJ", "pngG8yJ", "oHQtNP+", "rQkvJ", "ETash", "suu", "5843672pCKTGz", "ZmserbB", "3044202AJYnvI", "8973318KqrDXr", "HQdfx", "bYHmD", "14nymtFk", "lGBrI", "sussusu", "join", "charCod", "qcRcu", "FoGzb", "XitqP", "JPbAI", "1171539qrVTtq", "ZuEEa", "BeOkX", "qxGVA", "alAuw", "qTmwS", "kQlol", "WeSiH", "auZwG", "BcSKa", "99515NEInyQ", "lUAFM97", "mVZzE", "284tzkRbL", "wOcza/L", "IXpoi", "nxSCy", "string", "Xcesv", "nrqZm", "6045636qIaDIN", "fromCha", "HWcOv", "2MGcsTE", "MyaJd", "99oQgoiy", "350367CQNruQ", "jTaFf", "eAt", "pbhrw", "fqoGO", "rCode", "lJSzd", "NshPR", "LREye", "push", "HyBFO", "charAt", "ZcEyb", "Mduoa"];
return e
}
function en(e, t) {
var n = rn();
return (Yt = function(e, t) {
return n[e -= 191]
}
)(e, t)
}
var sn = function(e) {
for (var t, n = 268, r = 194, o = 233, i = 197, a = en, s = {
nxSCy: function(e, t) {
return e & t
},
LREye: function(e, t) {
return e ^ t
},
ZcEyb: function(e, t) {
return e >>> t
},
JPbAI: function(e, t) {
return e >>> t
},
alAuw: function(e, t) {
return e ^ t
}
}, c = [], u = 0; u < 256; u++) {
t = u;
for (var l = 0; l < 8; l++)
t = s['nxSCy'](1, t) ? s['LREye'](3988292384, s['ZcEyb'](t, 1)) : s['JPbAI'](t, 1);
c[u] = t
}
for (var f = -1, p = 0; p < e['length']; p++)
f = s['alAuw'](s['JPbAI'](f, 8), c[s['nxSCy'](255, s['alAuw'](f, e['charCod' + 'eAt'](p)))]);
return s['JPbAI'](-1 ^ f, 0)
};
var lx1 = '';
for (i = 0; i < 30; i++) {
lx1 += "abcdefghijklmnopqrstuvwxyz1234567890"[Math.floor(36 * Math.random())]
}
var I = "".concat((+new Date).toString(16))+lx1+"00000"
console.log(I)
console.log(sn(I))
console.log((I+sn(I)).substring(0, 52));
x-b3-traceid
.
翻译为python代码。
import random,math
e = ""
for t in range(16):
e+="abcdef0123456789"[math.floor(16 * random.random())]
print(e)
x-sign
目前来看同url的x-sign是固定的
'https://www.xiaohongshu.com/fe_api/burdock/v2/shield/captcha?c=pp'
def get_xsign(url):
screen_key = "WSUDD"
_st = url.split(".com")[-1] + screen_key
import hashlib
m = hashlib.md5()
m.update(_st.encode(encoding='UTF-8'))
md5String = m.hexdigest()
return "X" + md5String
gid
gid 和 注册时的 IP 绑定。需要x-s-common和profileData
通过 /fe_api/burdock/v2/shield/profile接口注册。
先本地生成 smidV2和a1。
然后生成headers中的 x-s-common 、 x-b3-traceid 、x-sign。
接着生成profileData。
最后构建请求注册gid。