短视频网站滑块分析

admin 2025年6月24日23:03:46评论7 views字数 15213阅读50分42秒阅读模式

最近本来有工作要研究这个滑块,但是一搜基本都是补环境的,觉得不够优雅,就尝试手撕一下JS VMP,好久没撕过就当练手了。

1

加密分析

提交接口抓包

当滑块松手后会调用接口kSecretApiVerify,传入verifyParam参数,这个参数是一个很长的base64,这里我初步猜测是对称加密。

加密定位

直接搜索verifyParam就可以定位到加密,这代码里面用了很多异步,所以看起来非常的恶心。

短视频网站滑块分析

最终我们可以定位到加密函数

短视频网站滑块分析

我解释一下这里,i = a.a[r("0x34")](c)是将c这个object进行json -> urlformencode的转换的,o = s[r("0x2d")](h, i)这一步是为了将i转为Uint8Array,方便下一步进行加密,s[r("0x2d")](x, o)这里就是执行加密的函数了。这一步往里面跟,发现调用了一个所谓的Jose的库,这个就是加密的VMP,这行代码还原后就是Jose.$encrypt([uint8ArrayData, fixedUuid])。

短视频网站滑块分析

这里的uint8ArrayData就是urlformencode后的数据,fixedUuid是个固定值,后面我们会讲它的作用,这里我们直接看Jose.$encrypt的实现。

JSVMP分析

往里继续跟就会到一个encrypt.js文件中, 一眼就看出是VMP了,VM的变量名没有被混淆。

短视频网站滑块分析

看到这里我们可以直接祭出Trace了,找到一些关键的操作我们可以直接插桩

c.prototype.lnot = function(t) {return !t},c.prototype.not = function(t) {return ~t},c.prototype.inc = function(t) {return t + 1},c.prototype.dec = function(t) {return t - 1},c.prototype.add = function(t, n) {return n + t},c.prototype.sub = function(t, n) {return n - t},c.prototype.mul = function(t, n) {return n * t},c.prototype.div = function(t, n) {return n / t},c.prototype.mod = function(t, n) {return n % t},c.prototype.shl = function(t, n) {return n << t},c.prototype.sar = function(t, n) {return n >> t},c.prototype.shr = function(t, n) {return n >>> t},c.prototype.or = function(t, n) {return n | t},c.prototype.and = function(t, n) {return n & t},c.prototype.xor = function(t, n) {return n ^ t},c.prototype.ceq = function(t, n) {return n == t},

向以上这些都是一些运算指令,因为我们要分析加密算法,所以将位操作全部插桩

new e("",(function(t, n, e, r) {var o = n.pop()        , i = n.pop();return null == o ? g(t, new Error("Cannot read property '" + i + "' of " + o)) : n.push(r.get(o, i))})), new e("",(function(t, n, e, r) {var o = n.pop()        , i = n.pop()        , u = n.pop();return null == o ? g(t, new Error("Cannot set property '" + i + "' of " + o)) : n.push(r.set(o, i, u))}

以上是访问object的对象的指令,我们也进行插桩

function(t, n, e, r, o) {var i = t.evalStack        , u = t.realm;if (null == r)return g(t, new Error("Cannot call method '" + e + "' of " + (void 0 === r ? "undefined" : "null")));var p = r.constructor.name || "Object";    u = u.get(r, e);return u instanceof Function ? l(t, n, u, r, o) : null == u ? (i.pop(),g(t, new Error("Object #<" + p + "> has no method '" + e + "'"))) : (i.pop(),g(t, new Error("Property '" + e + "' of object #<" + p + "> is not a function")))}), l = function(t, n, e, r, o, i) {if ("function" != typeof e)return g(t, new Error("object is not a function"));for (var u = t.evalStack, p = t.fiber, s = t.realm, c = {length: n,callee: e    }; n; )        c[--n] = u.pop();    r = r || s.global,    c = Array.prototype.slice.call(c);try {var a = i ? m(e, c) : e.apply(r, c);if (!p.paused)return u.push(a)    } catch (h) {g(t, h)    }}

以上是函数调用的指令,也可以进行插桩,方便我们分析,这里有个坑,我插桩了运算后就会导致浏览器卡死,所以我直接将代码抠到NodeJS里直接运行然后跑日志了。

分析Trace日志

因为我在分析之前就查阅了一些文章,这个滑块上一个版本用的是AES的加密,所以我尝试在tracelog中搜索了很多sbox的魔数以及rcon的操作,想直接把密钥还原出来,但是都没找到,这里浪费了很多时间。

转换verifyParam

这里我们可以直接将verifyParam转换为Uint8Array, 找到日志中生成的部分。最后发现这个verifyParam的结构通过concat是拼接出来的,非常像一个私有协议的实现,前64个byte都是头数据。

短视频网站滑块分析

[加密算法获取属性操作 -参数: { object: [Arguments] { '0':'00000000000000000003' }, property:0 }[加密算法获取属性操作 -参数: { object:'00000000000000000003'property:'length' }[加密算法获取属性操作 -参数: { object:'00000000000000000003'property:'substr' }[加密算法函数调用 -参数: {function: [Function:substr],context:'00000000000000000003',args: [ 02 ]}[加密算法函数调用结果: 3[加密算法获取属性操作 -参数: { object:'03'property:'length' }[加密算法获取属性操作 -参数: { object:Object [Math] {}, property:'pow' }[加密算法函数调用 -参数: {function: [Function:pow],context:Object [Math] {},args: [ 28 ]}[加密算法函数调用结果: 256[加密算法函数调用结果: 3[加密算法获取属性操作 -参数: {object: [00000,0000  ],property:'push'}[加密算法函数调用 -参数: {function: [Function:push],context: [00000,0000  ],args: [ 3 ]}[加密算法函数调用结果: 10[加密算法获取属性操作 -参数: { object:'00000000000000000003'property:'length' }[加密算法函数调用结果: [00000,00003][加密算法获取属性操作 -参数: { object: [ 222192173222320 ], property:'concat' }[加密算法函数调用 -参数: {function: [Function:concat],context: [ 222192173222320 ],args: [    [00000,00003    ]  ]}[加密算法函数调用结果: [222192173222320,0,   0,   0,   0,  00,0,   0,   0,   3]

以上只是头部部分拼接的日志,感兴趣可以自己去分析,这其中还包含了CRC32对数据的完整性校验,具体的包头的结构如下。

hexToLittleEndian('deadc0de'// deadcode魔数    + '20 00' // 0x20好像是固定值 00 = (((0x20 >>> 0) && ff) >>> 8) & ff    + '00 00 00 00 00 00 00 00 00 03' // 固定字符串 以上日志展示过    + '01 10' // 4097固定值 (4097 >>> 0) && 0xff), (4097 >>> 8) && 0xff)    + '06' // 固定值    + hexToLittleEndian(crc32(Buffer.from('c7b645db-65e8-401f-b38c-4c07c5fff247')).toString(16).padStart(8'0')) // 小端序 crc32 c7b645db-65e8-401f-b38c-4c07c5fff247 这个就是前面的fixedUuid    + '02' // 可能是版本号,也是个固定值    + hexToLittleEndian(crc32(Buffer.from(encryptData, 'hex')).toString(16).padStart(8'0')) // 小端序 数据crc32    + hexToLittleEndian((encryptData.length / 2).toString(16).padStart(8'0')) // 4字节数据长度, 小端序

包体加密

后续的拼接就是包体的加密了,这里也是分析很久,因为ks用了一个非常少见的算法,我甚至都是第一次听说这个算法。

这里的100就是原数据的uint8[加密算法] 获取属性操作 - 参数: { object: [Arguments] { '0': 100 }, property: 0 }[加密算法] 执行按位与操作, 输入: d9183ab9 & 1 输出: 1[加密算法] 执行按位与操作, 输入: d530d1b & 1 输出: 1[加密算法] 执行按位与操作, 输入: 50e74534 & 1 输出: 0[加密算法] 执行逻辑右移操作, 输入: 50e74534 >>> 1 输出: 2873a29a[加密算法] 执行按位与操作, 输入: 2873a29a & 7fffffff 输出: 2873a29a[加密算法] 执行按位与操作, 输入: d530d1b & 1 输出: 1[加密算法] 执行逻辑右移操作, 输入: 10000002 >>> 1 输出: 8000001[加密算法] 执行按位异或操作, 输入: d530d1b ^ 8000001 输出: 5530d1a[加密算法] 执行按位或操作, 输入: 5530d1a | f0000000 输出: f5530d1a[加密算法] 执行左移操作, 输入: 0 << 1 输出: 0[加密算法] 执行按位异或操作, 输入: 1 ^ 1 输出: 0[加密算法] 执行按位或操作, 输入: 0 | 0 输出: 0[加密算法] 执行按位与操作, 输入: 2873a29a & 1 输出: 0[加密算法] 执行逻辑右移操作, 输入: 2873a29a >>> 1 输出: 1439d14d[加密算法] 执行按位与操作, 输入: 1439d14d & 7fffffff 输出: 1439d14d[加密算法] 执行按位与操作, 输入: f5530d1a & 1 输出: 0[加密算法] 执行逻辑右移操作, 输入: f5530d1a >>> 1 输出: 7aa9868d[加密算法] 执行按位与操作, 输入: 7aa9868d & fffffff 输出: aa9868d[加密算法] 执行左移操作, 输入: 0 << 1 输出: 0[加密算法] 执行按位异或操作, 输入: 1 ^ 0 输出: 1[加密算法] 执行按位或操作, 输入: 0 | 1 输出: 1[加密算法] 执行按位与操作, 输入: 1439d14d & 1 输出: 1[加密算法] 执行逻辑右移操作, 输入: 80000062 >>> 1 输出: 40000031[加密算法] 执行按位异或操作, 输入: 1439d14d ^ 40000031 输出: 5439d17c[加密算法] 执行按位或操作, 输入: 5439d17c | 80000000 输出: d439d17c[加密算法] 执行按位与操作, 输入: d9183ab9 & 1 输出: 1[加密算法] 执行逻辑右移操作, 输入: 40000020 >>> 1 输出: 20000010[加密算法] 执行按位异或操作, 输入: d9183ab9 ^ 20000010 输出: f9183aa9[加密算法] 执行按位或操作, 输入: f9183aa9 | c0000000 输出: f9183aa9[加密算法] 执行左移操作, 输入: 1 << 1 输出: 2[加密算法] 执行按位异或操作, 输入: 1 ^ 0 输出: 1[加密算法] 执行按位或操作, 输入: 2 | 1 输出: 3[加密算法] 执行按位与操作, 输入: d439d17c & 1 输出: 0[加密算法] 执行逻辑右移操作, 输入: d439d17c >>> 1 输出: 6a1ce8be[加密算法] 执行按位与操作, 输入: 6a1ce8be & 7fffffff 输出: 6a1ce8be[加密算法] 执行按位与操作, 输入: aa9868d & 1 输出: 1[加密算法] 执行逻辑右移操作, 输入: 10000002 >>> 1 输出: 8000001[加密算法] 执行按位异或操作, 输入: aa9868d ^ 8000001 输出: 2a9868c[加密算法] 执行按位或操作, 输入: 2a9868c | f0000000 输出: f2a9868c[加密算法] 执行左移操作, 输入: 3 << 1 输出: 6[加密算法] 执行按位异或操作, 输入: 1 ^ 1 输出: 0[加密算法] 执行按位或操作, 输入: 6 | 0 输出: 6[加密算法] 执行按位与操作, 输入: 6a1ce8be & 1 输出: 0[加密算法] 执行逻辑右移操作, 输入: 6a1ce8be >>> 1 输出: 350e745f[加密算法] 执行按位与操作, 输入: 350e745f & 7fffffff 输出: 350e745f[加密算法] 执行按位与操作, 输入: f2a9868c & 1 输出: 0[加密算法] 执行逻辑右移操作, 输入: f2a9868c >>> 1 输出: 7954c346[加密算法] 执行按位与操作, 输入: 7954c346 & fffffff 输出: 954c346[加密算法] 执行左移操作, 输入: 6 << 1 输出: c[加密算法] 执行按位异或操作, 输入: 1 ^ 0 输出: 1[加密算法] 执行按位或操作, 输入: c | 1 输出: d[加密算法] 执行按位与操作, 输入: 350e745f & 1 输出: 1[加密算法] 执行逻辑右移操作, 输入: 80000062 >>> 1 输出: 40000031[加密算法] 执行按位异或操作, 输入: 350e745f ^ 40000031 输出: 750e746e[加密算法] 执行按位或操作, 输入: 750e746e | 80000000 输出: f50e746e[加密算法] 执行按位与操作, 输入: f9183aa9 & 1 输出: 1[加密算法] 执行逻辑右移操作, 输入: 40000020 >>> 1 输出: 20000010[加密算法] 执行按位异或操作, 输入: f9183aa9 ^ 20000010 输出: d9183ab9[加密算法] 执行按位或操作, 输入: d9183ab9 | c0000000 输出: d9183ab9[加密算法] 执行左移操作, 输入: d << 1 输出: 1a[加密算法] 执行按位异或操作, 输入: 1 ^ 0 输出: 1[加密算法] 执行按位或操作, 输入: 1a | 1 输出: 1b[加密算法] 执行按位与操作, 输入: f50e746e & 1 输出: 0[加密算法] 执行逻辑右移操作, 输入: f50e746e >>> 1 输出: 7a873a37[加密算法] 执行按位与操作, 输入: 7a873a37 & 7fffffff 输出: 7a873a37[加密算法] 执行按位与操作, 输入: 954c346 & 1 输出: 0[加密算法] 执行逻辑右移操作, 输入: 954c346 >>> 1 输出: 4aa61a3[加密算法] 执行按位与操作, 输入: 4aa61a3 & fffffff 输出: 4aa61a3[加密算法] 执行左移操作, 输入: 1b << 1 输出: 36[加密算法] 执行按位异或操作, 输入: 1 ^ 0 输出: 1[加密算法] 执行按位或操作, 输入: 36 | 1 输出: 37[加密算法] 执行按位与操作, 输入: 7a873a37 & 1 输出: 1[加密算法] 执行逻辑右移操作, 输入: 80000062 >>> 1 输出: 40000031[加密算法] 执行按位异或操作, 输入: 7a873a37 ^ 40000031 输出: 3a873a06[加密算法] 执行按位或操作, 输入: 3a873a06 | 80000000 输出: ba873a06[加密算法] 执行按位与操作, 输入: d9183ab9 & 1 输出: 1[加密算法] 执行逻辑右移操作, 输入: 40000020 >>> 1 输出: 20000010[加密算法] 执行按位异或操作, 输入: d9183ab9 ^ 20000010 输出: f9183aa9[加密算法] 执行按位或操作, 输入: f9183aa9 | c0000000 输出: f9183aa9[加密算法] 执行左移操作, 输入: 37 << 1 输出: 6e[加密算法] 执行按位异或操作, 输入: 1 ^ 0 输出: 1[加密算法] 执行按位或操作, 输入: 6e | 1 输出: 6f[加密算法] 执行按位异或操作, 输入: 64 ^ 6f 输出: b// 这个b就是对100这个原字节进行加密后的结果

这里我就发现这个加密好像是逐字节加密的,很明显的流加密算法,我以为是RC4,结果分析了以后发现完全不一样,我就一直找到第一个字节加密的trace,发现有一个初始出来的魔数,然后通过这个魔数继续往上翻,找到了这个byte初始化的位置。

[加密算法] 获取属性操作 - 参数: { object'BvWTr0uRBGH366Yb', property: 12 }[加密算法] 获取属性操作 - 参数: { object'6', property: 'charCodeAt' }[加密算法] 函数调用 - 参数: { function: [Function: charCodeAt], context: '6', args: [ 0 ] }[加密算法] 函数调用结果: 54[加密算法] 设置属性操作 - 参数: {object: [66,118,87,84,114,48,117,82,66,71,72,51,    <4 empty items>  ],  property: 12,  value: 54}[加密算法] 获取属性操作 - 参数: { object'BvWTr0uRBGH366Yb', property: 13 }[加密算法] 获取属性操作 - 参数: { object'6', property: 'charCodeAt' }[加密算法] 函数调用 - 参数: { function: [Function: charCodeAt], context: '6', args: [ 0 ] }[加密算法] 函数调用结果: 54[加密算法] 设置属性操作 - 参数: {object: [66,118,87,84,114,48,117,82,66,71,72,51,54,    <3 empty items>  ],  property: 13,  value: 54}[加密算法] 获取属性操作 - 参数: { object'BvWTr0uRBGH366Yb', property: 14 }[加密算法] 获取属性操作 - 参数: { object'Y', property: 'charCodeAt' }[加密算法] 函数调用 - 参数: { function: [Function: charCodeAt], context: 'Y', args: [ 0 ] }[加密算法] 函数调用结果: 89[加密算法] 设置属性操作 - 参数: {object: [66,118,87,84,114,48,117,82,66,71,72,51,54,54,    <2 empty items>  ],  property: 14,  value: 89}[加密算法] 获取属性操作 - 参数: { object'BvWTr0uRBGH366Yb', property: 15 }[加密算法] 获取属性操作 - 参数: { object'b', property: 'charCodeAt' }[加密算法] 函数调用 - 参数: { function: [Function: charCodeAt], context: 'b', args: [ 0 ] }[加密算法] 函数调用结果: 98[加密算法] 设置属性操作 - 参数: {object: [66,118,87,84,114,48,117,82,66,71,72,51,54,54,89,    <1 empty item>  ],  property: 15,  value: 98}[加密算法] 执行左移操作, 输入: 13579bdf << 8 输出: 579bdf00[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 4}[加密算法] 执行按位或操作, 输入: 579bdf00 | 72 输出: 579bdf72 // key1  第一轮[加密算法] 执行左移操作, 输入: 2468ace0 << 8 输出: 68ace000[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 4}[加密算法] 执行按位或操作, 输入: 68ace000 | 72 输出: 68ace072 // key2 第一轮[加密算法] 执行左移操作, 输入: fdb97531 << 8 输出: b9753100[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 4}[加密算法] 执行按位或操作, 输入: b9753100 | 72 输出: b9753172[加密算法] 执行左移操作, 输入: 579bdf72 << 8 输出: 9bdf7200[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 5}[加密算法] 执行按位或操作, 输入: 9bdf7200 | 30 输出: 9bdf7230[加密算法] 执行左移操作, 输入: 68ace072 << 8 输出: ace07200[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 5}[加密算法] 执行按位或操作, 输入: ace07200 | 30 输出: ace07230[加密算法] 执行左移操作, 输入: b9753172 << 8 输出: 75317200[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 5}[加密算法] 执行按位或操作, 输入: 75317200 | 30 输出: 75317230[加密算法] 执行左移操作, 输入: 9bdf7230 << 8 输出: df723000[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 6}[加密算法] 执行按位或操作, 输入: df723000 | 75 输出: df723075[加密算法] 执行左移操作, 输入: ace07230 << 8 输出: e0723000[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 6}[加密算法] 执行按位或操作, 输入: e0723000 | 75 输出: e0723075[加密算法] 执行左移操作, 输入: 75317230 << 8 输出: 31723000[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 6}[加密算法] 执行按位或操作, 输入: 31723000 | 75 输出: 31723075[加密算法] 执行左移操作, 输入: df723075 << 8 输出: 72307500[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 7}[加密算法] 执行按位或操作, 输入: 72307500 | 52 输出: 72307552[加密算法] 执行左移操作, 输入: e0723075 << 8 输出: 72307500[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 7}[加密算法] 执行按位或操作, 输入: 72307500 | 52 输出: 72307552[加密算法] 执行左移操作, 输入: 31723075 << 8 输出: 72307500[加密算法] 获取属性操作 - 参数: {object: [66118878411448,117,  826671,  7251,54,  548998  ],  property: 7}[加密算法] 执行按位或操作, 输入: 72307500 | 52 输出: 72307552

这里可以看到,会通过一个种子和三个魔数(0x13579bdf,0x2468ace0,0xfdb97531)来计算一个key(0x72307552),然后通过这个key来加密原数据,于是我找了很多资料去查这三个魔数,最后确定了是cfmxCompat的算法,很多年就已经过气的算法了,这中间踩了很多坑,因为关于这个算法的文章非常少,实现也少,甚至还有很多变体,只能一点点去对这Trace比结果,这个算法应该是ks自己去实现的,我这里就不展开了,有兴趣的可以自己去查资料,还原了这个算法整个加密就出来了。

2

加密/解密算法还原

class CfmxCompat {constructor() {this.m_LFSR_A = 0x13579bdf;this.m_LFSR_B = 0x2468ace0;this.m_LFSR_C = 0xfdb97531;this.m_MASK_A = 0x80000062;this.m_MASK_B = 0x40000020;this.m_MASK_C = 0x10000002;this.m_ROT0_A = 0x7fffffff;this.m_ROT0_B = 0x3fffffff;this.m_ROT0_C = 0xfffffff;this.m_ROT1_A = 0x80000000;this.m_ROT1_B = 0xc0000000;this.m_ROT1_C = 0xf0000000;    }/**     * Encrypt a value     *     * @param {String} string - String to encrypt.     * @param {String} key - Key or seed used to encrypt the string.     * @param {String} [encoding=hex] - The binary encoding in which to represent the data as a string. Can be either 'hex' or 'base64'.     * @returns {String} The encrypted value.     */    encrypt(inString, key, encoding = 'hex') {        let inBytes = Buffer.from(inString);        let outString = this.transformString(inBytes, key);if (encoding === 'base64') {            outString = outString.toString('base64');        } else if (encoding === 'hex') {            outString = outString.toString('hex').toUpperCase();        } else {throw new Error('Invalid encoding');        }return outString;    }/**     * Decrypt a value     *     * @param {String} string - String to decrypt.     * @param {String} key - The key or seed that was used to encrypt the string.     * @param {String} [encoding=hex] - The binary encoding in which to represent the data as a string. Must be the same as the algorithm used to encrypt the string.     * @returns {Buffer} The decrypted value.     */    decrypt(inString, key, encoding = 'hex') {        let inBytes;if (encoding === 'base64') {            inBytes = Buffer.from(inString, 'base64');        } else if (encoding === 'hex') {            inBytes = Buffer.from(inString, 'hex');        } else {throw new Error('Invalid encoding');        }        let outBytes = this.transformString(inBytes, key);return outBytes.toString();    }    transformString(inBytes, key) {this.m_LFSR_A = 0x13579bdf;this.m_LFSR_B = 0x2468ace0;this.m_LFSR_C = 0xfdb97531;this.setKey(key);        let length = inBytes.length;        let outBytes = Buffer.alloc(length);for (let i = 0; i < length; ++i) {            let byte = inBytes.readInt8(i, true);            outBytes[i] = this.transformByte(byte);        }return outBytes;    }    transformByte(target) {        let crypto = 0;        let b = this.m_LFSR_B & 0x1;        let c = this.m_LFSR_C & 0x1;for (let i = 0; i < 8; ++i) {if (0 !== (this.m_LFSR_A & 0x1)) {this.m_LFSR_A = this.m_LFSR_A ^ this.m_MASK_A >>> 1 | this.m_ROT1_A;if (0 !== (this.m_LFSR_B & 0x1)) {this.m_LFSR_B = this.m_LFSR_B ^ this.m_MASK_B >>> 1 | this.m_ROT1_B;                    b = 1;                } else {this.m_LFSR_B = this.m_LFSR_B >>> 1 & this.m_ROT0_B;                    b = 0;                }            } else {this.m_LFSR_A = this.m_LFSR_A >>> 1 & this.m_ROT0_A;if (0 !== (this.m_LFSR_C & 0x1)) {this.m_LFSR_C = this.m_LFSR_C ^ this.m_MASK_C >>> 1 | this.m_ROT1_C;                    c = 1;                } else {this.m_LFSR_C = this.m_LFSR_C >>> 1 & this.m_ROT0_C;                    c = 0;                }            }            crypto = crypto << 1 | b ^ c;        }        target = target ^ crypto;return target;    }    setKey(key) {        key = key || 'Default Seed';        let i;        let keyLength = key.length;        let seed = new Array(Math.max(12, keyLength));for (i = 0; i < keyLength; i++) {            seed[i] = key[i].charCodeAt(0);        }for (i = 0; keyLength + i < 12; ++i) {            seed[keyLength + i] = seed[i];        }for (i = 0; i < 4; i++) {this.m_LFSR_A = (this.m_LFSR_A << 8) | seed[i + 4];this.m_LFSR_B = (this.m_LFSR_B << 8) | seed[i + 4];this.m_LFSR_C = (this.m_LFSR_C << 8) | seed[i + 4];        }if (0 === this.m_LFSR_A) this.m_LFSR_A = 0x13579bdf;if (0 === this.m_LFSR_B) this.m_LFSR_B = 0x2468ace0;if (0 === this.m_LFSR_C) this.m_LFSR_C = 0xfdb97531;    }}new CfmxCompat().encrypt("will encrypt data"'BvWTr0uRBGH366Yb''hex'// 加密new CfmxCompat().decrypt("will decrypt data"'BvWTr0uRBGH366Yb''hex'// 解密

尾声

到这里verifyParam的加密就还原完了,但是这里面还有个轨迹需要补,不过在52那边有人分享过这个轨迹的生成,虽然有一点变动,但是大体上还是一样的,这里我就不展开了。

短视频网站滑块分析

看雪ID:Mysterious77

https://bbs.kanxue.com/user-home-943408.htm

*本文为看雪论坛优秀文章,由 Mysterious77原创,转载请注明来自看雪社区
短视频网站滑块分析
议题征集中!看雪·第九届安全开发者峰会

#

原文始发于微信公众号(看雪学苑):短视频网站滑块分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月24日23:03:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   短视频网站滑块分析http://cn-sec.com/archives/4198885.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息