本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号【K哥爬虫】联系作者立即删除!
最近有小伙伴博客留言,询问关于雪球 1038 长串的相关逆向思路,该参数整体难度中等,有些细节地方会导致最后生成的参数无法使用,在了解长串之前先分析一下基础的短串(某坤行),由简到繁。非业务项目,本文仅对该参数进行逆向分析,仅供学习交流。
某坤行/某雪球/acw_sc__v2
aHR0cHM6Ly93d3cuemtoLmNvbS9pdGVtL0FBNjI0MjI5MS5odG1s
aHR0cHM6Ly94dWVxaXUuY29tLw==
aHR0cHM6Ly93d3cudGhmdW5kLmNvbS5jbi8=
首先访问详情页,发现他会请求 2 次,第一次返回带混淆的 js 代码:
第二次请求,携带 md5__1101
参数即可返回正常的 html 内容:
某雪球长串短串随机触发(大部分是长串),我们这里点击评论列表的小气泡图标,发起数据请求,其中载荷参数为主要分析对象:
本文由易到难,从短串到长串依次进行分析。
首先我们访问详情页,下脚本断点,发现他在 VMxxx.html
里成功断下,这里发现 href 暂未生成相关参数:
单步跟栈,最终发现在末尾调用 m[l1(Gh.GI)](j, m[l1(Gh.GV)](H, m[l1(Gh.GO)](T, b[l1(Gh.Gp)][l1(Gh.GT)])))
方法,生成了新的 href 参数:
进入 j 函数,首先通过 sig 函数生成值后,再拼接时间戳等生成新参数,1482973743|0|1757665216486|1
,将对应的 sig 算法扣下即可:
'sig': function(I) {
var lU = l1;
for (var V = -0x51b * -0x1 + 0x2051 * -0x1 + 0x1b36, O = m[lU(Go.l)](encodeURIComponent, I), W = 0x1 * 0x102d + -0x1 * -0x1b0f + 0x4 * -0xacf; m[lU(Go.m)](W, O[lU(Go.G)]); W++)
V = m[lU(Go.k)](m[lU(Go.Q)](m[lU(Go.t)](m[lU(Go.b)](V, 0xa78 + -0x662 * 0x2 + 0x1 * 0x253), V), 0x1647 + -0x32e * 0x8 + 0x11 * 0x47), O[lU(Go.Y)](W)),
V |= -0xfe1 * 0x2 + -0x1c9d + 0x3c5f;
return V;
}
随后将拼接好的参数经过 F['ua'](V, !(-0x1c67 + 0x544 + 0x1723))
方法生成最终的参数:
进入 ua 函数,是个平坦流,发现其主要生成逻辑为 uu,D 为固定值:
最终经过 z['uu']
将 I 值与匿名函数传入,生成 1101 值,只需将 uu 模块整体扣下即可:
最后,携带正确的 1101 参数即可完成请求:
进入评论列表,打开 F12 触发了无限 debugger,这个 debugger,之前也有粉丝问过,类型如下:
向上查看堆栈,发现是进入了大 Function,主要逻辑就是 s['charCodeAt'](0);
,在其中插入 debugger 导致字符转 Unicode 编码时断下:
将 js 修改为 (Xg = M['E'][XD(j4.A)][XD(j4.h)][XD(j4.m) + 'r']('s', 'i', R[XD(j4.V)](R[XD(j4.b)](R[XD(j4.Q)](H), XR), R[XD(j4.w)])),Xi[XR]["charCodeAt"](0))
利用逗号表达式的特性将这个 debugger 过掉。
随后我们就要找到这个 js 是在哪里动态注入的,同 1101 参数思路一样,下脚本断点后刷新页面,将我们修改后的 js 文本替换到 Gq.text
中:
再次刷新,发现 js 没有触发 debugger,但是浏览器卡死了,说明发现 js 被我们手动修改了。
向上排查,可疑位置如下:
修改后会导致 js 发生改变,导致这里 ME 不等于 997,我们将这里也一并修改,让 ME 恒等于 997,返回正常逻辑。再次替换,刷新页面即可正常调试(首页第一次请求 html 中 script 里也会携带 js,同理替换,换汤不换药,这里不做区分。后续评论接口可按上述方法替换分析)。
这个属于代码执行过程中产生的 debugger,也可以直接用魔改浏览器过掉,可以参考:
浏览器魔改-从根本上通杀所有的无限debugger:https://mp.weixin.qq.com/s/up0frzR2Fl9ijtHikkfs7Q
按抓包分析中的步骤,我们点击评论按钮,进入到下图 send 堆栈处:
经过 GE 方法后,openArgs
中携带了 1038 参数,继续跟进,发现进入到了类似 1101 参数的生成逻辑,但是比其更为复杂:
进入到加密函数 XY 中,发现是 switch 与 case 重重嵌套:
这里不做解混淆,利用 1101 的思路去慢慢跟进,首先是对 params 进行了拼接然后编码了一下:
其中 _waf_bd8ce2ce37
是前置接口返回的:
随后继续单步跟进,发现它魔改了 Math 函数:
经过研究发现,类似固定随机因子到自写函数,使得 Math.random 可以得到自己想要的,进入 p 函数,发现每次置入随机因子的时候,F 都会初始化一个值(这个可以用做观察点,可以下断点看看走完全程需要置入多少次随机因子):
同时,发现在 F 值重置的时候,会调用 z 函数来生成 XR 值,在 z 函数内则是 Error 堆栈检测,它会检测你是哪个函数调用报错的:
堆栈不同则最后生成的 XR 不同(随机因子与 F 值初始化有关系,同时又与 XR 值有关系,XR 为错误堆栈检测)。最终在这些值的加成下,让 random 可以返回想要的值,如下图:
随后调用 X8 函数生成 Xb 值:
然后调用 XQ = R[ZJ(M4.H)](X8, Xm))
方法,继续由 x8 生成 XQ,这里 random 还是一直轮着的,前面出现错误,那么后面生成的参数都是错的。
最终调用 XX, X0
完成 xb 值的拼接:
Xb = [R[ZJ(M4.v)](XX, Xb), R[ZJ(M4.F)](XX, XQ), R[ZJ(M4.p)](X0), R[ZJ(M4.e)](new M['E'][(ZJ(M4.k))]()[ZJ(M4.G)](), ''), XV[ZJ(M4.J) + ZJ(M4.d)], XV[ZJ(M4.n) + ZJ(M4.S)], R[ZJ(M4.c)](f)][ZJ(M4.W)]('|');
主要讲的就 X0,里面检测居多,其中最经典的就是调试或篡改检测,进入 X0 函数,首先到了检测函数 L:
第一个便是反调试检测,如下:
检测代码如下:
Xg = 0
var Xj = new Error()
, XR = {};
XR["configurable"] = !(-0x2ed * -0xa + -0x1 * -0x647 + -0x2388);
XR["enumerable"] = !(0x833 + 0x1e9 + -0xa1b * 0x1);
XR["get"] = function () {
console.log("触发")
return Xg = -0x1 * -0x791 + -0x775 + -0x10 * 0x40 + 997,
'';
}
,
(Object["defineProperty"](Xj, "stack", XR), console.debug(Xj))
console.log(Xg)
正常运行这段代码会将 Xg 的值设置为 1,即被检测,过掉这个检测且不影响原代码的情况下,只需加一行 console.debug=function (){}
即可过掉,如果不加,你最后扣下来的 1038 是用不了的。
随后进入到了一系列 node 的环境检测:
因为我们是扣代码,所以只要照着浏览器去改就行了,全部弄好后,最终生成的 X0 值才是可用的。
最后调用 J 函数,将拼接后的 Xb 传入即可生成最终的值,但是这里还有一个小坑,正常加密的时候,码表应该是 T 开头的,但是如果没有处理码表,加密出来的参数会对应不上:
最终溯源发现码表经过了修改,先修改随机因子:
然后 G 函数通过已随机的因子将码表打乱,重新排序,得到正确的码表:
结果:
acw_sc__v2
的难度在 1101 与 1038 之间,能搞定 1038 那么新 v2 自然也是可以的,还是熟悉 debugger,这也是粉丝提问的:
还是老办法,进行 js 替换:
替换后用 Hook 进行堆栈追踪,发现最终的生成位置如下:
向上分析逻辑,发现还是先进行随机设置(分析到后文不难看出这又是另一种混淆,让它返回想返回的随机数,与固定参数组合生成想要的值),T 的话,分析可知,仍然还是由 arg1 按照指定规则生成的,T = arg1[yX(oK.p)](-0x1640 + 0x701 * 0x2 + 0x1 * 0x5ec + oO, -0x9e4 + -0x53 * 0x35 + -0x38f * -0x7 + oO)[yX(oK.X)]('')
:
首先进入魔改后的 fiil 函数,将数组进行规则打乱后返回:
随后利用设置好的随机数再次将打乱后的数据按照指定规则排序(这里可以记录随机数):
再拼接随机文本数字,最终转小写后拼接生成,不管是 1101 还是 1038 亦或者 acw_sc__v2
参数,最终算法也都仅几十行不过百行:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。