评论请求中的a_bogus参数
根据堆栈下断点,这里要多找几个地方,太靠后了的话不是需要的断点
当断在图中所示位置时,发现e的属性值里面包含了a_b参数
继续看上层函数,发现r的属性值里也有a_b参数
在这个函数开始的地方打下断点,但是运行之后发现断下来的次数太频繁了,这里使用条件断点
1 | r._tnc_request_url.includes("a_bogus") |
断下来之后跟到上层函数,发现此时arguments里面就携带了a_b参数
继续往上,发现是这里的p携带了参数,函数的参数e也有a_b,还得继续往上找
发现这里的e值里有a_b
同样的方法往上找,断在这里的时候this也有a_b参数
再往上找,这里的e里携带了目标参数,但是不建议在这里下断点,因为在vmp里面,下断点之后网页直接卡死了
在vmp的上面断点
可以推测m.send(p)这里的m经过后面的那段vmp的处理之后,就生成了a_b参数
通过断点的现象也可以判断,刚执行到这里的时候,m里面还没有任何值,执行完了就有了,说明关键代码就在上面看到的vmp里面,也就是x、d、N三个函数
先把整个js的代码给扣下来,而且这牵涉到一个问题,就是这里的X函数是在vmp里面,没法像其他的逆向一样,有一整个加密函数,然后扣代码之后直接调用封装好的加密函数就行了,通过断点也可以验证这个猜想,断在X(e, this, arguments, r)之后,不是每次断点都是执行的加密的那一步
可以看到,两次断点结果,r的值都不一样
现在要考虑的就是代码扣出来了,怎么把这个X函数给导出,就需要找加密时执行的X函数的特征,通过执行发现,当执行到加密时的X函数的时候,参数e的值是固定的,可以通过转字符串来进行判断之后导出到全局
1 | function D(t, r) { |
后面就是正常补环境,现在就是怎么去调用的问题
上层函数是m.send(p);,然后调用了n函数,m.send(p) 实际调用的是 n(p),也就是 send = n,而 n 的函数体是调用 X(e, this, arguments, r),最终调用的是 X 函数,X函数里的this就是上下文m,arguments就是p,所以可以通过window.aaa.call(ctx, p)或者window.aaa.apply(ctx, p)来调用,这里的ctx就是this(XMLHTTPrequest的实例)
1 | let nn = new XMLHttpRequest(); |
然后运行之后调试进入return X(e, this, arguments, r)这里,现在的问题是怎么拿到返回值,通过调试可以发现,当return X(e, this, arguments, r)被调用之后,this(也就是this,XMLHTTPRequest的实例)的secureOpenArgs的值中就会携带了ab参数
可以看到刚进入X函数的时候,此时secureOpenArgs的值中还没有ab参数
可以通过hook来找secureOpenArgs什么时候被赋值的,以下代码仅作用于步入X函数之后r对象还没有secureOpenArgs属性的时候
1 | Object.defineProperty(r, 'secureOpenArgs', { |
然后就蒙蔽了,此时调试发现r刚进函数的时候已经有secureOpenArgs字段了,因为代理的关系只能重新调试,这个时候是在未登录状态下进行调试的,未登录状态的加密函数和基本逻辑没有变,只是X的上层函数变了,可以直接在X那里打条件断点
1 | typeof this === 'object' && |
然后就发现未登录状态下,ab参数的生成之后的值不是放在secureOpenArgs里面了,而是执行之后丢到this的_url属性里面去了
而且调试发现刚进X函数的时候,r对象还没有_url属性,所以可以hook下来
1 | Object.defineProperty(r, '_url', { |
hook之后发现是在var m = n.apply(d, e);这里进行赋值的,当然是在hook之后的函数的上面好几层函数
然后通过打印d和e,定位到e的生成,打印参数就行了
1 | var m = n.apply(d, e); |
几个注意的点,一是传参的时候,p是[null]而不是null,二是msToken的值要放到localStorage的xmst属性里(hook的时候会打印)或者直接丢参数里面
1 | let msToken = 'xxxxxxx'; |



















