feat: makecookie command support remote url gerente cookie

This commit is contained in:
rnet 2024-01-05 02:26:06 +08:00
parent ccbcbcc3fa
commit 76e2d20d96
13 changed files with 106 additions and 93 deletions

View File

@ -1,4 +1,4 @@
这个项目是瑞数加密的逆向研究,代码开发基于网站链接`https://wcjs.sbj.cnipa.gov.cn/sgtmi`
该项目为瑞数加密的逆向研究,代码开发基于网站:`https://wcjs.sbj.cnipa.gov.cn/sgtmi`
研究包括动态代码生成原理及动态cookie生成原理。
@ -6,7 +6,9 @@
## 0. 声明
该项目下代码仅用于个人学习、研究或欣赏。通过使用该仓库相关代码产生的风险与仓库代码作者无关。
该项目下代码仅用于个人学习、研究或欣赏。通过使用该仓库相关代码产生的风险与仓库代码作者无关!
该项目的研究网站仅做参考,项目不鼓励直接请求该研究网站,算法逆向研究请直接使用`example`目录下的样例文件,如:`node main.js makecookie -f example/codes/1-\$_ts.json`
该瑞数cookie生成过程中的算法逆向仍存在以下问题
@ -22,7 +24,7 @@
## 2. 瑞数算法还原
**`npx rs-reverse *`与在当前目录下运行`node main.js *`相对应**
**`npx rs-reverse *`与在当前目录下运行`node main.js *`相对应, 当然也支持npm全局安装(`npm install -g rs-reverse`)npm全局安装后也可以直接使用命令`rs-reverse`**
如npx运行的包不是最新的可以加上-p参数后执行如`npx -p rs-reverse@latest rs-reverse makecookie`,非官方源可能存在版本不同步问题,建议拉取时使用官方源:`--registry=https://registry.npmjs.org`
@ -35,10 +37,7 @@ npm包不能保证最新代码最新代码以仓库代码为准!
1. npx方式`npx rs-reverse makecode`
2. 文件方式:`node main.js makecode`
如运行:`npx rs-reverse makecode -u http://wcjs.sbj.cnipa.gov.cn/sgtmi`,后生成三个文件:
1. 动态代码文件`output/dynamic-code.js`
2. `$_ts`文件`output/input_ts.json``output/output_ts.json`
**命令后不接参数则从example文件中取**
```console
$ npx rs-reverse makecode -h
@ -49,6 +48,7 @@ npm包不能保证最新代码最新代码以仓库代码为准!
Options:
-h 显示帮助信息 [boolean]
-f, --file 含有nsd, cd值的json文件 [string]
-l, --level 日志打印等级参考log4js默认为info [string]
-u, --url 瑞数返回204状态码的请求地址 [string]
-v, --version 显示版本号 [boolean]
@ -60,24 +60,23 @@ Examples:
调用示例:
```bash
$ npx rs-reverse makecode
$ npx rs-reverse makecode -u https://wcjs.sbj.cnipa.gov.cn/sgtmi
输入ts./output/output_ts.js
输出ts./output/output_ts.js
动态代码:./output/dynamic_code.js/input_ts.json
输出ts/path/to/output/makecode_output_ts.json
输出动态代码:/path/to/output/makecode_output_code.js
url方式提取的ts/path/to/output/makecode_input_ts.json
url方式提取的静态文本/path/to/output/makecode_input_immucfg.json
```
### 2.2. makecookie子命令
执行子命令`makecookie`生成动态代码, 可以传入包含`$_ts.nsd``$_ts.cd`的文本文件或者直接给url让程序自己去拿, 命令示例:
执行子命令`makecookie`生成cookie, 调用方式与`makecode`类型,调用示例:
1. npx方式`npx rs-reverse makecookie`
2. 文件方式:`node main.js makecookie`
该命令首先会执行`makecode`子命令拿到完整的`$_ts`再运行makecookie算法生成cookie。
`makecookie`命令与`makecode`使用方式类似:
该命令首先会执行`makecode`子命令拿到完整的`$_ts`值,再运行`makecookie`的还原算法生成cookie。
```console
$ npx rs-reverse makecookie -h
@ -88,7 +87,9 @@ rs-reverse makecookie
Options:
-h 显示帮助信息 [boolean]
-f, --file 含有nsd, cd值的json文件 [string]
-l, --level 日志打印等级参考log4js默认为info [string]
-u, --url 瑞数返回204状态码的请求地址 [string]
-a, --adapt 已经做了适配的网站名称不传则为cnipa [string]
-v, --version 显示版本号 [boolean]
Examples:
@ -99,16 +100,19 @@ Examples:
调用示例:
```bash
$ npx rs-reverse makecookie
$ npx rs-reverse makecookie -u https://wcjs.sbj.cnipa.gov.cn/sgtmi
Cookie值: 06F5jRMo1hoEoEPOKmB6eAcYaJeWGRuYthzufuuJ4tkmcCMZLbouHGcYIm462aiARAwGlJIvr6M_vbi1K3kaer3n0ZE459rr3eKe8U.xTOaCdVxik6TW2XIle7cmudenUkPfU4UaEGoqOl8nAv38f_Qb_g6kZMpHn51J2qsIdscBqGgjQvcwWPFWk2r9SHbAJbRganpjSeTH1PksisGcKwHCFbHsXqYX1jAW672tcssl
url方式提取的ts/path/to/output/makecookie_url_ts_1704391389883.json
url方式提取的静态文本/path/to/output/makecookie_url_immutext_1704391389883.json
Cookie值: 0yk64LrpoFnc8Wi4Mmu_rijgRRoC2SHY1bQlR2_QZ805_CqRd1uOgGRnlEvHvXSoQuwkx_fwn4iQnPDFrQigm1b4GnD61Pf9vU5XKtJtAWIoWeG_22OLiccUwGjI0lQaJ_jaYIBFygNsPSPf_0GnJyT1umFrFgAkAoqh1s0G9IDE1uPEM3PV8M1J.wbKdSgMLg8T3bGD5w2sHHohKfnwsT7bMNbb8xbjSxsn8qb8AvY0
Cookie长: 236
```
### 2.3. exec子命令
runcode子命令用于开发中或者演示时使用。命令示例:
exec子命令用于开发中或者演示时使用。命令示例:
1. npx方式`npx rs-reverse exec -c 'gv.cp2'`
2. 文件方式:`node main.js exec -c 'gv.cp2'`
@ -132,27 +136,15 @@ Examples:
调用示例:
```bash
$ npm rs-reverse exec -c '+ascii2string(gv.keys[21])'
$ npx rs-reverse exec -c '+ascii2string(gv.keys[21])'
输入:+ascii2string(gv.keys[21])
输出1718026159
```
## 3. 手动获取动态代码和$_ts的方法
## 3. 技术交流
目录`example/codes/`下的文件为手动保存,用于验证代码功能,如运行:`npm run test`后会比对程序生成的动态代码与`$_ts`文件是否与相关静态文件文本内容一致。
当然也可以自己手动拿动态代码和$_ts以验证程序是否还有效可以通过控制台拿到相关文本
1. 在文件中`http://wcjs.sbj.cnipa.gov.cn/c5rxzYrjRT2h/cCdzB9ZjDFks.294cc83.js`找到代码`_$_q.call(_$gP, _$_y)`并打入断点(文件找不到可以通过其它两种方法定位);
2. 找到如第一条的js文件搜索`.call(`找到调用方法;
2. 通过代理cookie变动的方式打断点通过堆栈找到调用方法。
断点后复制相关文本:
1. 拿到动态代码:`copy(_$_y)`
2. 拿到`$_ts`: `console.log(JSON.stringify(window.$_ts))`,这里有点蒙,可以用`JSON.stringify(window.$_ts)`或者`copy(JSON.stringify(window.$_ts))`试试
*初始的`$_ts`可以在这个文件入口处打断点获取。*
加作者微信进技术交流群: `howduudu_tech`(备注rs-reverse)
**后续开源补环境框架群内第一时间通知**

View File

@ -1,6 +1,6 @@
{
"name": "rs-reverse",
"version": "1.1.3",
"version": "1.2.0",
"description": "瑞数算法逆向,website reverse engineering",
"main": "main.js",
"directories": {

View File

@ -44,7 +44,10 @@ class GlobalVarible {
// 返回密钥集合
return cache.keys;
}
setAttr(attr, value) {
_getAttr(attr) {
return cache[attr];
}
_setAttr(attr, value) {
cache[attr] = value;
if (attr === 'cp0') {
cache.cp0_96 = _chunk(value, 96);

View File

@ -48,6 +48,6 @@ function makeBignum() {
}
exports.init = function() {
gv.setAttr('bignum', makeBignum());
gv.setAttr('cfgnum', makeCfgnum());
gv._setAttr('bignum', makeBignum());
gv._setAttr('cfgnum', makeCfgnum());
}

View File

@ -14,12 +14,12 @@ module.exports = function(...params) {
}
}
function dynamicExec(task, start = 0, args = [], loop_res = [], global_res = []) {
args = monitor(args, 'args', { getLog: true, setLog: true });
loop_res = monitor(loop_res, 'loop_res', { getLog: true, setLog: true, getCb: (key) => {if(['81', '83'].includes(key))debugger}});
global_res = monitor(global_res, 'global_res', { getLog: true, setLog: true });
if (typeof task === 'string') task = gv.r2mka(task).taskarr;
logger.trace(`动态代码运行,任务列表:${task}, 起点:${start},长度:${task.length}`);
function dynamicExec(taskItem, start = 0, args = [], loop_res = {}, global_res = {}) {
const { key, taskarr: task } = taskItem;
args = monitor(args, '${key}_args', { getLog: true, setLog: true });
loop_res = monitor(loop_res, `${key}_loop_res`, { getLog: true, setLog: true });
global_res = monitor(global_res, `${key}_global_res`, { getLog: true, setLog: true });
logger.trace(`动态代码运行,任务列表:${key}, 起点:${start},长度:${task.length}`);
const data = [];
const ret = [];
ret[0] = args;
@ -32,7 +32,7 @@ function dynamicExec(task, start = 0, args = [], loop_res = [], global_res = [])
const len = task.length;
const notCheckTask = [11, 15, 27, 32, 48, 51, 59, 84, 104, 109];
for (t_cursor = start; t_cursor < len; t_cursor++) {
// console.log(`${notCheckTask.includes(task[t_cursor]) ? '[no]' : '[ok]'}【${++runtimes}, ${t_cursor}】运行 case${task[t_cursor]}`);
logger.trace(`${notCheckTask.includes(task[t_cursor]) ? '[no]' : '[ok]'}${++runtimes}, ${t_cursor}${key} 运行 case${task[t_cursor]}`);
switch (task[t_cursor]) {
case 0:
temp1 = data[--d_cursor];
@ -89,7 +89,6 @@ function dynamicExec(task, start = 0, args = [], loop_res = [], global_res = [])
case 11:
// temp3 = task[++t_cursor];
// data[d_cursor++] = ret[3][temp3][task[++t_cursor]];
notCheckTask.push(task[t_cursor]);
break;
case 12:
return data[--d_cursor];
@ -107,7 +106,6 @@ function dynamicExec(task, start = 0, args = [], loop_res = [], global_res = [])
case 15:
// temp3 = task[++t_cursor];
// data[d_cursor++] = ret[1][temp3][task[++t_cursor]];
notCheckTask.push(task[t_cursor]);
break;
case 16:
d_cursor--;
@ -189,7 +187,6 @@ function dynamicExec(task, start = 0, args = [], loop_res = [], global_res = [])
case 32:
// _$cG(_$$I, task[++t_cursor], task[++t_cursor], temp2 = task[++t_cursor], task[++t_cursor], t_cursor + 1, ret[2], ret);
// ret[4] ? t_cursor = len : t_cursor += temp2;
notCheckTask.push(task[t_cursor]);
break;
case 33:
tarkey = task[++t_cursor];
@ -271,7 +268,6 @@ function dynamicExec(task, start = 0, args = [], loop_res = [], global_res = [])
case 48:
// tarkey = task[++t_cursor];
// data[d_cursor++] = _$_w(_$$I._$hW[tarkey], ret);
notCheckTask.push(task[t_cursor]);
break;
case 49:
d_cursor -= 2;
@ -289,7 +285,6 @@ function dynamicExec(task, start = 0, args = [], loop_res = [], global_res = [])
// d_cursor -= temp2;
// setTarget();
// data[d_cursor++] = _$fH(target[tarkey], temp4);
notCheckTask.push(task[t_cursor]);
break;
case 52:
temp1 = data[--d_cursor];
@ -325,7 +320,6 @@ function dynamicExec(task, start = 0, args = [], loop_res = [], global_res = [])
// temp1 = _$kG[tarkey];
// task[t_cursor] = temp1;
// data[d_cursor++] = temp1;
notCheckTask.push(task[t_cursor]);
break;
case 60:
temp1 = data[--d_cursor];
@ -446,7 +440,6 @@ function dynamicExec(task, start = 0, args = [], loop_res = [], global_res = [])
// temp2 = target[temp1];
// temp2 == _$an ? temp2 = task[++t_cursor] : ++t_cursor;
// t_cursor += temp2;
notCheckTask.push(task[t_cursor]);
break;
case 85:
setTarget();
@ -543,7 +536,6 @@ function dynamicExec(task, start = 0, args = [], loop_res = [], global_res = [])
// temp3 = task[++t_cursor];
// tarkey = task[++t_cursor];
// target = ret[1][temp3];
notCheckTask.push(task[t_cursor]);
break;
case 105:
tarkey = task[++t_cursor];
@ -571,7 +563,6 @@ function dynamicExec(task, start = 0, args = [], loop_res = [], global_res = [])
// d_cursor -= temp2;
// setTarget();
// temp1 = _$fH(target[tarkey], temp4);
notCheckTask.push(task[t_cursor]);
break;
case 110:
temp1 = data[--d_cursor];

View File

@ -4,8 +4,8 @@ const logger = require('@utils/logger');
function init(ts, r2mkaText) {
const startTime = new Date().getTime();
gv.setAttr('utils', common);
gv.setAttr('ts', ts);
gv._setAttr('utils', common);
gv._setAttr('ts', ts);
require('./r2mka').init(r2mkaText);
require('./tscp').init();
require('./tscd').init();

View File

@ -37,6 +37,7 @@ const parse = (() => {
} else {
val.taskarr = str.split('').map(it => it.charCodeAt());
}
val.taskori = val.taskarr; // taskarr会被动态修改因此存一个备份
val.key = `${deeps.join('>')}-${count++}`;
valMap[val.key] = val;
val.child_one.map((it, idx) => {
@ -85,6 +86,6 @@ exports.parse = function(str = gt3) {
};
exports.init = function(r2mkaText) {
gv.setAttr('r2mka', exports.parse(r2mkaText || gt3));
gv._setAttr('r2mka', exports.parse(r2mkaText || gt3));
logger.debug('头r2mka标识字符串完成解析!')
}

View File

@ -0,0 +1,7 @@
const gv = require('@src/handler/globalVarible');
module.exports = {
'0>one>21-23': (num) => {
return Math.abs(num) % gv.cp2[52];
}
}

View File

@ -4,6 +4,8 @@ const decrypt = require('./common/decrypt');
const logger = require('@utils/logger');
const numToNumarr4 = require('./common/numToNumarr4');
const dynamicExec = require('./common/dynamicExec');
const custask = require('./task');
const error = require('@utils/error');
function getValMaps() {
let uid = gv.ts.cp[3];
@ -18,52 +20,51 @@ function getValMaps() {
function parse(arr) {
const len = arr.length;
const valMap = getValMaps();
let idx = 0;
const task = [57, 132, 132, 132, 132, 132, 132].reverse();
let idx = 4;
do {
// 逻辑概率来源为idx初始值为4此处为idx += gv.cp2[2], 如果之后task有变可尝试固定为该逻辑
idx += gv.cp2[gv.r2mka("0>one>5-5").taskarr[task.pop()]];
idx += gv.cp2[2];
const mod = idx % (len - idx > gv.cp2[2] ? gv.cp2[2] : len - idx);
arr[mod + idx] ^= valMap[mod];
} while (idx < len && task.length);
if (mod + idx < arr.length) {
arr[mod + idx] ^= valMap[mod];
}
} while (idx < len);
return arr;
}
function getTaskarr(arr, idx, ans = []) {
function getTaskarr(arr, idx, ans = {}) {
if (idx >= arr.length) return;
const start = idx + 1;
const end = start + arr[idx];
ans.push(arr.slice(start, end));
const key = arr[idx - 2];
ans[key] = gv.r2mka().child_one[key].taskarr = arr.slice(start, end);
getTaskarr(arr, end + 2, ans);
return ans;
}
function getOffset(arr) {
const [task0, task1, task2, task3] = getTaskarr(arr, arr[3]);
logger.trace([task0, task1, task3]);
const taskMap = getTaskarr(arr, arr[3]);
const l81 = gv.r2mka("0-0").taskarr[42] + gv.cp2[56];
const l83 = (gv.cp0_96(1, gv.r2mka("0-0").taskarr[57]).charCodeAt() + l81) * l81;
const offset = dynamicExec('0>one>22-24', 0, undefined, {
20: function(...params) {
return dynamicExec(task0, 0, params, {
8: function (...params) {
return dynamicExec(task1, 0, params);
}
})
},
8: function(...params) {
return dynamicExec(task1, 0, params);
},
10: function(...params) {
return dynamicExec(task3, 0, params);
},
81: l81,
83: l83,
}, {
const l82 = gv.cp0_96(1, gv.r2mka("0-0").taskarr[57]).charCodeAt() + l81;
const l83 = l82 * l81;
const global_res = {
59: gv.cp2,
};
const loop_res = gv.r2mka().child_one.map((it, idx) => {
if (!it) return it;
return (...params) => {
if (taskMap[idx]) {
return dynamicExec(it, 0, params, loop_res, global_res);
}
if (custask[it.key]) {
return custask[it.key](...params);
}
error(1000, { key: it.key }); // 任务方法未适配终止方法运行
}
})
return offset;
loop_res[81] = l81;
loop_res[82] = l82;
loop_res[83] = l83;
return dynamicExec(gv.r2mka('0>one>22-24'), 0, undefined, loop_res, global_res);
}
exports.init = function() {
@ -72,8 +73,10 @@ exports.init = function() {
const end = (cdArr[0] << gv.cp2[52] | cdArr[1]) + start;
const one = parse(cdArr.slice(start, end)); // arr127
const offset = getOffset(one);
gv._setAttr('dynamicTaskOffset', offset);
gv._setAttr('dynamicTask', getTaskarr(one, one[3]));
const ans = cdArr.slice(end).map((item, idx) => { // arr1575
return item ^ offset[idx % gv.cp2[52]];
return item ^ offset[idx % gv.cp2[52]];
})
const keys = []
for (let i = 0, op = 1; i < ans[0]; i ++) {
@ -81,6 +84,6 @@ exports.init = function() {
keys.push(ans.slice(op, op + gap));
op += gap;
}
gv.setAttr('keys', keys);
gv._setAttr('keys', keys);
logger.debug('$_ts.cd完成解析!')
};

View File

@ -4,8 +4,8 @@ const parse = require('./common/main');
const logger = require('@utils/logger');
exports.init = function() {
gv.setAttr('cp0', parse(gv.ts.cp[0]));
gv.setAttr('cp2', parse(gv.ts.cp[2]).map(Number));
gv.setAttr('cp6', parse(gv.ts.cp[6]));
gv._setAttr('cp0', parse(gv.ts.cp[0]));
gv._setAttr('cp2', parse(gv.ts.cp[2]).map(Number));
gv._setAttr('cp6', parse(gv.ts.cp[6]));
logger.debug('$_ts.cp完成解析!')
}

View File

@ -4,6 +4,7 @@ const Cookie = require('./handler/Cookie');
const unescape = require('@utils/unescape');
const paths = require('@utils/paths');
const fs = require('fs');
const gv = require('@src/handler/globalVarible');
function parseR2mka(text) {
const start = text.indexOf('"') + 1;
@ -32,6 +33,7 @@ function writefile(ts, immucfg) {
}
module.exports = function (ts, immucfg) {
gv._setAttr('_ts', ts);
if (immucfg) writefile(ts, immucfg);
const startTime = new Date().getTime();
const coder = new Coder(ts, immucfg);

14
utils/error.js Normal file
View File

@ -0,0 +1,14 @@
const gv = require('@src/handler/globalVarible');
const logger = require('./logger');
const errorMap = {
9999: '未知报错!',
1000: '解码cd值发现未定义任务方法',
}
module.exports = function(type, data = {}) {
Object.assign(data, gv._getAttr('_ts'));
data.errType = type;
logger.fatal(`(${type})程序出错啦请将该段报错文本贴到issue中${JSON.stringify(data)}`);
throw new Error(errorMap[type || 9999])
}

View File

@ -13,7 +13,7 @@ const appDirectory = (() => {
const resolveApp = (...relativePath) => path.resolve(appDirectory, ...relativePath);
module.exports = {
basePath: resolveApp(''),
basePath: resolveApp('.'),
modulePath: resolveApp('node_modules'),
binPath: resolveApp('node_modules', '.bin'),
package: resolveApp('package.json'),