国际惯例 今天帮朋友看一个gov网站的瑞数加密(天津电子税x局)
瑞数6特征
1.服务器会发两次包,第一次响应状态码为412,第二次响应状态码为200。
2.有三重debugger,其中有一重debugger要拦截和修改使用 Function 构造函数创建的新函数(即Hook)。
3.返回第一个cookie_S其中cookie_S的值的第一个值为6代表瑞数6代(由服务器生成);然后js混淆生成了第二个cookie_T,只有携带有效的cookie_T才能正确请求页面状态码才是200。(也有些是O/P结尾的)
4.加载VM的1万多行代码入口特性用正则匹配是:ret=\S{4}.call(\S{4},(\S{4}));其中第一个参数为eval,第二个是要用eval执行生成的VM文件。
这里简单提一句怎么区分瑞数3/4/5/6/jsvmp版本的
如图中后端返回的第一个LmqtOhuon8XES键值对,里面的首个字符串是6,就代表了当前为6代,同理也有3/4/5开头的,至于jsvmp版本,则可以通过响应源码里面有如下内容$_ts.nsd 或者 打开f12直接在跳出的js里面搜索<= 63 ,也可以判断出来它是最新版本的瑞数jsvmp。
废话不多说直接开干吧 我们首先分析下整个流程
index文件
外链JS文件(自执行方法)
分析1.其中meta标签,其content内容很长且(每次请求都是动态变化的)。
分析2.外链js文件,一般同一页面中其内容是固定的,里面是个自执行方法,虚拟文件入口也在这里,主要是解密ts内容。
分析3.ts内容(每次请求首页都会动态变化)
执行流程图
接下来进入喜闻乐见的nodejs补环境环节
ps:瑞数6在扣代码到本地环境的时候,千万不要格式化代码,因为瑞数6有to string检测,会检测代码的长度、内容或者一些方法的环境。不然逆向会送你一个“Bug大礼包”Invalid array length 程序一直执行。(不仅是瑞数,其它的都是一样的,只要是js文件,在扣js代码的内容,最好是碗就碗是碟就碟,千万不要格式化)。
这里推荐几个大佬的开源代码
这里贴部分补环境的代码
window = new Proxy(window, {
get: function (x, y) {
if (typeof x[y] === "function") {
x[y].toString = function () {
return 'function ' + y + '() { [native code] }'
}
}
return x[y]
},
set: function (target, p, value, receiver) {
if (p.indexOf('jsonp_') > -1) {
callbackJsonpCustom.push(p)
}
Object.defineProperty(target, p, {value: value, writable: true, configurable: true});
// Object.defineProperty(global, p, {value: value, writable: true, configurable: true});
return true
}
});
window.location = new Proxy(window.location, {
get: function (x, y) {
if (typeof x[y] === "function") {
x[y].toString = function () {
return 'function ' + y + '() { [native code] }'
}
}
if (y === "port") {
return "443"
}
return x[y]
},
set: function (target, p, value, receiver) {
if (p.indexOf('jsonp_') > -1) {
callbackJsonpCustom.push(p)
}
Object.defineProperty(target, p, {value: value, writable: true, configurable: true});
return true
}
});
let txt = fs.readFileSync('./JsBase64.txt').toString('utf8')
let ts_data = new Buffer.from(txt, 'base64').toString('utf8')
window['$_ts'] = {};
let execScript = window.eval;
window.eval.toString = function () {
return 'function eval() { [native code] }'
}
window.eval = function (data) {
window.eval = execScript;
window.eval.toString = function () {
return 'function eval() { [native code] }'
}
这里需要注意一个东西(必须加上)
delete __dirname;
delete __filename;
另外再提几句比较坑的地方,有时候补环境生成的长度比较短,然后拿去跑代码,会发现后端返回http状态码400,这个时候就得注意下是否环境没补全,看看document.all 的检测 / webdriver 属性检测 / navigator 相关属性检测 等 能从浏览器自吐的就全部cv过去。
ok 到了喜闻乐见的成品运行图。(目前可以通杀所有省份的税务局瑞数6代jsvmp)