前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >xhs-web校验流程分析

xhs-web校验流程分析

作者头像
李玺
发布2022-11-14 17:18:14
1.6K0
发布2022-11-14 17:18:14
举报
文章被收录于专栏:爬虫逆向案例

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

xhsFingerprintV3VERSION: ‘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可正常访问。


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。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-11-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 流程概述
  • timestamp2
  • 滑块验证参数
    • Params
      • 轨迹FN
        • x5生成
        • x8生成
        • x9生成
    • captcha deviceId
    • ProfileData
    • x-s-common
    • smidV2
    • a1
    • x-b3-traceid
    • x-sign
    • gid
    相关产品与服务
    验证码
    腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档