声明:该文章为学习使用,严禁用于商业用途和非法用途,违者后果自负,由此产生的一切后果均与作者无关
一、瑞数四特点:
- 有两次相同的请求。
- 瑞数 Cookie 一般是成对出现的,例如 FSSBBIl1UgzbN7N80S 和 FSSBBIl1UgzbN7N80T。其中以 S 结尾的 Cookie 是第一次的 202 那个请求服务器返回的,以 T 结尾的 Cookie 是由 JS 生成的,动态变化的,T 和 S 前面一般会跟 80 或 443 的数字 (数字 80 是 http 协议的默认端口号,数字 443 是 https 协议的默认端口)。
- js 代码生成的 cookie 值,首字母一定会有一个数字,3 代表 3 代,4 表示 4 代,5 代表 5 代。
- 打开抓包工具后,会进入两次无限 debugger。
- 第一次请求:状态码 202, 服务器响应一个 cookie, 返回一段包含 js 代码的 html 代码。
第一次返回的 html 代码结构:- meta 标签的 content 内容,每次都是变化的。- 两个 script 标签:- 第一个 script 标签是外链的 JS,直接打开看是乱码的,里面定义了全局变量 window.$_ts,在自执行方法中会用到,在不同页面也有所差别,但是同一个网站同一个页面 JS 里的内容一般是固定不会变的。- 第二个 script 标签里面包含一个自执行方法,可以将外链的 JS 乱码变成 VM 文件中的正常代码,用来生成 cookie 信息, 每次请求变化的只是变量名,整体逻辑不变,瑞数的 JS 混淆代码中,变量、方法名大多类似于 _$xx,有众多的 if-else 控制流。
6、第二次请求,携带两个 cookie 返回正确数据 。
二、瑞数4分析
本文介绍的是JS逆向补环境的方式过瑞数
目标网站:http://www.fangdi.com.cn/index.html
1、请求状态码
清空cookie, 刷新页面,可以看到先请求了http://www.fangdi.com.cn/index.html 接口,返回了状态码202

2、返回代码构成
返回的是一段包含js代码的html代码,其中js代码是一个自执行方法,用来生成cookie信息,而且还有一个外链的js,请求后返回的是ts代码


3、定位cookie位置
清空cookie后,使用油猴脚本
(function () {
// 严谨模式 检查所有错误
'use strict';
// document 为要hook的对象 这里是hook的cookie
var cookieTemp = "";
Object.defineProperty(document, 'cookie', {
// hook set方法也就是赋值的方法
set: function (val) {
// 这样就可以快速给下面这个代码行下断点
// 从而快速定位设置cookie的代码
if (val.indexOf('FSSBBIl1UgzbN7N80T') != -1) {
debugger;
}
console.log('Hook捕获到cookie设置->', val);
cookieTemp = val;
return val;
}, // hook get 方法也就是取值的方法
get: function () {
return cookieTemp;
}
});
})();
依次出现两个典型的无限 debugger对于后续分析没有影响,直接右键永不在此暂停,定位到cookie

找到cookie赋值位置,由于很多值、变量都是动态变化的,所以需要固定一套代码到本地,方便打断点分析和跟栈

4、分析代码
在查看栈堆时,我们发现上面是同一个 VM 文件,下面都在 index.html 文件中。VM 文件是浏览器为匿名函数创建的内存空间,用于存储和执行临时脚本,通常由执行
eval
、
setTimeOut
、
call
等方法生成。所以可以确定一定是在 index.html 中调用方法生成了 VM 文件。
通过定位到 index.html 文件,发现是执行了
call
方法生成了 VM 文件(瑞数 4、5、6 通常都是调用的
call
方法)。向上查找,会发现
call
方法在自执行方法里面。
由于代码执行完后,在
document.cookie
中一定会有数据。此时,我们可以直接把整个自执行方法拿下来,然后使用
console.log(document.cookie)
直接调用
cookie
,在控制台输出。

5、补环境
运行代码,会提示缺少环境
_$yz = window,
^
ReferenceError: window is not defined
使用吐环境代码:
function get_enviroment(proxy_array) {
for (var i = 0; i < proxy_array.length; i++) {
handler = '{\n' +
' get: function(target, property, receiver) {\n' +
' console.log("方法:", "get ", "对象:", ' +
'"' + proxy_array[i] + '" ,' +
'" 属性:", property, ' +
'" 属性类型:", ' + 'typeof property, ' +
// '" 属性值:", ' + 'target[property], ' +
'" 属性值类型:", typeof target[property]);\n' +
' return target[property];\n' +
' },\n' +
' set: function(target, property, value, receiver) {\n' +
' console.log("方法:", "set ", "对象:", ' +
'"' + proxy_array[i] + '" ,' +
'" 属性:", property, ' +
'" 属性类型:", ' + 'typeof property, ' +
// '" 属性值:", ' + 'target[property], ' +
'" 属性值类型:", typeof target[property]);\n' +
' return Reflect.set(...arguments);\n' +
' }\n' +
'}'
eval('try{\n' + proxy_array[i] + ';\n'
+ proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}catch (e) {\n' + proxy_array[i] + '={};\n'
+ proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}')
}
}
proxy_array = ['window', 'document', 'location', 'navigator', '
版权归原作者 終归于心 所有, 如有侵权,请联系我们删除。