xhs-web校验流程分析

2022-11-14 17:18:14 浏览数 (1)

经测试,cookie中需携带gid和timestamp2。参数整理有点乱,仅供参考。

xhsFingerprintV3VERSION: ‘2.1.2’


文章目录
  • 流程概述
  • timestamp2
  • 滑块验证参
    • Params
    • 轨迹FN
  • captcha deviceId
  • ProfileData
  • x-s-common
    • x5生成
    • x8生成
    • x9生成

  • smidV2
  • a1
  • x-b3-traceid
  • x-sign
  • gid


流程概述

初次无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可正常访问。


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。

定位调试。

代码语言:javascript复制
'{"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:

代码语言:javascript复制
navigator.mediaDevices.enumerateDevices()
  .then((list) => {
    console.log(list);
  });

这个Id每次访问会变更,可以自己随机生成一串。

所在设备在被限制时,可更换canvasId和enumerateDevices的Id。


x-s-common

x-s-common用的地方比较多,注册和校验都需要。

x-s-common定位。

x-s-common的生成主要依赖cookie中的smidV2,其他参数如下。

x5: Dt.a.get(“a1”), x8: localStorage.getItem(“b1”), x9: fn(P()(t = P()(n = “”.concat®).call(n, o)).call(t, localStorage.getItem(“b1”)))


x5生成

x5依赖cookie的a1参数

代码语言:javascript复制
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)

代码语言:javascript复制
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', h['nrqZm'])) {
                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)

格式化一下

代码语言:javascript复制
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生成代码

代码语言:javascript复制
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代码。

代码语言:javascript复制
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’

代码语言:javascript复制
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。

0 人点赞