能够走到这儿的,也就不难找到本题所需的逆向参数是cookie里的m和RM值。
一、逆向位置定位
(1)RM值定位
通过油猴hook脚本,hook脚本可以在我前几个文章里找,不难定位到这儿(一定要是RM有值的地方往前追)。也就是RM= _0x4e96b4['_$ss'],鼠标悬停发现 _0x4e96b4是window。那我们就hook window的'_$ss'的属性。
// ==UserScript==
// @name hookwindow
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author Shirmay1
// @run-at document-start
// @match https://match.yuanrenxue.cn/match/5
// @grant none
// ==/UserScript==
(function() {
'use strict';
var pre = "";
Object.defineProperty(window, '_$ss', {
get: function() {
console.log('Getting window.属性');
return pre
},
set: function(val) {
console.log('Setting window.属性', val);
debugger ;
pre = val;
}
})
})();
通过油猴不难来到这儿
稍微分析下_0x4e96b4['_$ss']是一个通过AES加密后的密文,其中_0x4e96b4['_$pr']是明文数据,一个数组列表,_0x4e96b4["_$qF"]是密钥数据,一个对象。又是window的两个属性。
(2)RM明文定位
继续hook。密钥大概率是固定的,先hook明文_0x4e96b4['_$pr']。
_0x4d2d2c=Array,也就是创建一个空列表,而我们的明文数据包含五个元素,那么肯定是进行了push操作,搜一下_0x4e96b4['_$pr'],看看有没有push的地方。一共有三处地方进行了push,但第一处很奇怪。push完又删除,先都打到断点看看,走那里。
刷新可以看到_0x4e96b4['_$pr']['push'](_0x474032(_$Wa));这一行走了四次,向_0x4e96b4['_$pr']添加了四个元素,每次传入的是_0x474032(_$Wa)这个函数生成值,该函数的参数是_$Wa = _0x12eaf3(),往上追一下发现是调用DATE函数生成时间戳。这就很奇怪了,随时生成的时间戳是个变量啊,而且这个变量看上下文并没有赋值给其它参数。也就是说这四次生成的元素,服务器是没法校验的。同时在这里我们也发现了m赋值的地方。
最后一次生成的地方在这儿,至此五个值刚好完成,本次push的还是_0x474032(_$yw)这个函数,这次传入的参数也是时间戳,但同时时间戳也赋值给了_0x4e96b4['_$is'],同时我们也看到了m值生成的地方,而且在断点完全释放后发的数据包里的m值就是这次的m值。因此我们大胆的猜测发包时同时携带着_0x4e96b4['_$is'],用这三个值服务器进行校验。
(3)请求体定位
一开始我们以为请求体的m和f两个值就是一个时间戳,不需要逆向,但是在RM值分析的过程中其传入的参数也是时间戳,那这三个时间戳应该是有关系 ,服务器才好进行校验。
大胆猜测,我们就追一下看看。直接在启动器里查看request栈。
果然m=window._$is,这也就确定了服务器用请求体m、cookie的RM值和cookie.m值三者进行校验。
(4)RM密钥定位
继续用hook来定位密钥也就是window._$qF生成的地方,发现其是对window._$is也就是data.m进行切片生成的,原来密钥也是动态的。
回顾一下整体逻辑:RM明文、RM密钥、data.m,cookie.m这四者的关系应该是这样。
rm = new Array()
for (i = 0; i < 4; i++) {
timer = new Date().getTime()
rm.push(_0x474032(timer))
}
data_m = new Date().getTime()
cookie_m = _0x474032(data_m)
rm.push(_0x474032(cookie_m))
key=CryptoJS['enc']['Utf8']['parse'](window['btoa'](window['_$is'])['slice'](0x0, 0x10))
_$Ww = _$Tk['enc']['Utf8']['parse'](rm['toString']()),
_0x29dd83 = _$Tk['A' + "ES"]['encrypt'](_$Ww, key, {
'mode': _$Tk['mode']['ECB'],
'padding': _$Tk['pad']['Pkcs7']
}),
cookie_rm= _0x29dd83['toString']()
二、cookie.RM生成
先把明文和密钥写死来验证下是不是标准的aes加密。
没有问题,是标准的aes加密。
三、cookie.m生成
cookie.m=_0x474032(时间戳),也就是重点扣_0x474032这个函数。同样先写个验证函数,再一点一点的扣。
function _0x474032(_0x233f82, _0xe2ed33, _0x3229f9) {
return _0xe2ed33 ? _0x3229f9 ? v(_0xe2ed33, _0x233f82) : y(_0xe2ed33, _0x233f82) : _0x3229f9 ? _0x41873d(_0x233f82) : _0x37614a(_0x233f82);
}
timer=1713411300000
m=_0x474032(timer)
if (m=="c1b570b8df5e42f035821111d7b22df7") {
console.log('大功告成')
}else{
console.log('再接再厉');
}
缺什么补什么就不说,主要说些有争议的,第一个错误history没定义,定位到这儿发现在一个try的catch里,大概率是不走不到那儿的,浏览器调试下试试。果然没走,而且多刷新几次发现
op = _0x4e96b4['$_zw'][_$UH[0x6c]]是定值27,直接改写。
改写后就直接跳过了,
定位发现在一个eval里,浏览器执行下看看b64pad = _0x4e96b4['$_zw'][9]['length']也是个定值1,直接改写,同样也跳过了
函数补完不出意外,来到了再接再厉
先看看函数里的try都是怎么走的。
这个try,浏览器运行是走try里面的,还原下是这样的,var _0x42fb36 = window['XMLHttpRequest']['prototype']['DONE'] * 0x4;,那这个原型链里的东西,肯定是定值。直接改写。
try处理完还是再接再厉,那就可能还是缺少环境,我们只补了一个window,对window挂上代理看看
function proxy_watch(obj) {
return new Proxy(obj, {
get(target, p, receiver) {
debugger;
let val = Reflect.get(...arguments);
if (val==undefined){
// console.log("get", target, "=获取属性>", p, "=值>", val);
// console.log('=========')
// console.log('=========')
console.log("get", "=获取属性>", 'window.'+p, "=值>", val);
}
return val;
},
set(target, p, value, receiver) {
debugger;
// console.log("set", target, "=设置属性>", p, "=值>", value);
return Reflect.set(...arguments);
}
});
};
window = proxy_watch(window)
可以看到取了四个window的属性未定义,浏览器控制台输出看看是什么 。
这两个定义了,补上。
大功告成!
至此cookie.m的值逆向完成。
四、编写调用函数
function get_data() {
rm = new Array()
for (i = 0; i < 4; i++) {
timer = new Date().getTime()
rm.push(_0x474032(timer))
}
data_m = new Date().getTime()
cookie_m = _0x474032(data_m)
rm.push(cookie_m)
key = CryptoJS['enc']['Utf8']['parse'](window['btoa'](window['_$is'])['slice'](0x0, 0x10))
_$Ww = _$Tk['enc']['Utf8']['parse'](rm['toString']()),
_0x29dd83 = _$Tk['A' + "ES"]['encrypt'](_$Ww, key, {
'mode': _$Tk['mode']['ECB'],
'padding': _$Tk['pad']['Pkcs7']
}),
cookie_rm = _0x29dd83['toString']()
data_f = Date.parse(new Date())
return [data_m, data_f, cookie_m, cookie_rm]
}
剩下的就是py编写了!