mirror of
https://github.com/pysunday/rs-reverse.git
synced 2025-04-12 03:46:56 +08:00
feat: immutext automatic extraction
This commit is contained in:
parent
45588d148f
commit
96068ddc28
47
main.js
47
main.js
@ -11,6 +11,8 @@ const pkg = require(paths.package);
|
||||
const request = require('request-promise');
|
||||
const cheerio = require('cheerio');
|
||||
const log4js = require('log4js');
|
||||
const urlresolve = require('url').resolve;
|
||||
const adapt = require('@src/adapt');
|
||||
|
||||
function debugLog(level) {
|
||||
if (level) {
|
||||
@ -25,6 +27,24 @@ function debugLog(level) {
|
||||
logger.trace('paths:\n', JSON.stringify(paths, null, 2));
|
||||
}
|
||||
|
||||
const getCode = async (url) => {
|
||||
if (!isValidUrl(url)) throw new Error('输入链接不正确');
|
||||
const res = await request(url)
|
||||
const $ = cheerio.load(res);
|
||||
const scripts = [...$('script')]
|
||||
const tsscript = scripts.map(ele => $(ele).text()).filter(text => text.includes('$_ts.nsd') && text.includes('$_ts.cd'));
|
||||
if (!tsscript.length) throw new Error('链接返回结果未找到cd或nsd');
|
||||
const $_ts = Function('window', tsscript[0] + 'return $_ts')({});
|
||||
const checkSrc = (src) => src?.split('.').pop() === 'js' ? src : undefined;
|
||||
const remotes = scripts.map(it => checkSrc(it.attribs.src)).filter(Boolean);
|
||||
if (!remotes.length) throw new Error('未找到js外链,无法提取配置文本请检查!');
|
||||
for(let src of remotes) {
|
||||
const jscode = await request(urlresolve(url, src));
|
||||
if (jscode.includes('r2mKa')) return { $_ts, jscode };
|
||||
}
|
||||
throw new Error('js外链中没有瑞数的代码文件');
|
||||
}
|
||||
|
||||
const commandBuilder = {
|
||||
f: {
|
||||
alias: 'file',
|
||||
@ -45,28 +65,25 @@ const commandBuilder = {
|
||||
alias: 'url',
|
||||
describe: '瑞数返回204状态码的请求地址',
|
||||
type: 'string',
|
||||
coerce: async (input) => {
|
||||
if (!isValidUrl(input)) throw new Error('输入链接不正确');
|
||||
try {
|
||||
const res = await request(input)
|
||||
const $ = cheerio.load(res);
|
||||
const scripts = [...$('script')].map(ele => $(ele).text())
|
||||
.filter(text => text.includes('$_ts.nsd') && text.includes('$_ts.cd'));
|
||||
if (!scripts.length) throw new Error('链接返回结果未找到cd或nsd');
|
||||
return Function('window', scripts[0] + 'return $_ts')({});
|
||||
} catch(err) {
|
||||
throw new Error('输入链接无效');
|
||||
}
|
||||
}
|
||||
coerce: getCode,
|
||||
},
|
||||
a: {
|
||||
alias: 'adapt',
|
||||
describe: '已经做了适配的网站名称,不传则为cnipa',
|
||||
type: 'string',
|
||||
}
|
||||
}
|
||||
|
||||
const commandHandler = (command, argv) => {
|
||||
debugLog(argv.level);
|
||||
const ts = argv.file || argv.url || require(paths.exampleResolve('codes/1-\$_ts.json'));
|
||||
const ts = argv.url?.$_ts || argv.file || require(paths.exampleResolve('codes/1-\$_ts.json'));
|
||||
logger.trace(`传入的$_ts.nsd: ${ts.nsd}`);
|
||||
logger.trace(`传入的$_ts.cd: ${ts.cd}`);
|
||||
command(ts);
|
||||
if (argv.url) {
|
||||
command(ts, adapt(argv.url.jscode, argv.adapt));
|
||||
} else {
|
||||
command(ts);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = yargs
|
||||
|
6
src/adapt/cnipa/index.js
Normal file
6
src/adapt/cnipa/index.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
cp0: 'gl}y|sMnyn}',
|
||||
cp2: 'g+*`-+`+0`.1`',
|
||||
globalText1: "'r2mKa'.length",
|
||||
globalText2: '&Ţfunction',
|
||||
}
|
38
src/adapt/index.js
Normal file
38
src/adapt/index.js
Normal file
@ -0,0 +1,38 @@
|
||||
const fs = require('fs');
|
||||
const unescape = require('@utils/unescape');
|
||||
|
||||
const adapts = fs.readdirSync(__dirname, { withFileTypes: true })
|
||||
.filter(file => file.isDirectory())
|
||||
.map(folder => folder.name)
|
||||
.reduce((ans, it) => {
|
||||
ans[it] = require(`./${it}/`);
|
||||
return ans;
|
||||
}, {});
|
||||
|
||||
function findFullString(text, start, end) {
|
||||
let startIdx, endIdx;
|
||||
do {
|
||||
if (startIdx <= 0 || endIdx >= text.length - 1) return;
|
||||
startIdx === undefined && start--;
|
||||
endIdx === undefined && end++;
|
||||
if (startIdx === undefined && text[start] === '"' && text[start - 1] !== '\\') {
|
||||
startIdx = start;
|
||||
}
|
||||
if (endIdx === undefined && text[end] === '"' && text[end - 1] !== '\\') {
|
||||
endIdx = end;
|
||||
}
|
||||
} while (startIdx === undefined || endIdx === undefined);
|
||||
return unescape(text.slice(startIdx + 1, endIdx));
|
||||
}
|
||||
|
||||
module.exports = function(code, name) {
|
||||
const config = adapts[name] ? adapts[name] : adapts.cnipa;
|
||||
const idx = code.indexOf(config.cp2);
|
||||
return Object.entries(config).reduce((ans, [key, val]) => {
|
||||
const idx = code.indexOf(val);
|
||||
if (code.indexOf(val, idx + val.length) > -1) throw new Error(`${key}对应的值${val}在代码中非唯一,请检查!`);
|
||||
const fullString = findFullString(code, idx, idx + val.length);
|
||||
return { ...ans, [key]: fullString };
|
||||
}, {});
|
||||
}
|
||||
|
24
src/adapt/readme.md
Normal file
24
src/adapt/readme.md
Normal file
@ -0,0 +1,24 @@
|
||||
**该目录用于适配瑞数网站使用**
|
||||
|
||||
需要手动给出每个网站的静态加密串的特征值(值内部取一段不会重复的文本,尽量不要二进制文本,容易复制黏贴过程中被编辑器转译)
|
||||
|
||||
将对应的特征串放入相应变量名下:
|
||||
|
||||
1. cp0: `$_ts.cp[0]`对应的加密文本;
|
||||
2. cp2: `$_ts.cp[2]`对应的加密文本;
|
||||
3. globalText1: 生成动态代码过程中第1段用到的加密文本;
|
||||
4. globalText2: 生成动态代码过程中第2段用到的加密文本;
|
||||
|
||||
**一定要保证复制的串是唯一的,可以先搜索确认只搜索到一处!!!**
|
||||
|
||||
示例:
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
cp0: 'gl}y|sMnyn}',
|
||||
cp2: 'g+*`-+`+0`.1`',
|
||||
globalText1: "'r2mKa'.length",
|
||||
globalText2: '&Ţfunction',
|
||||
}
|
||||
```
|
||||
|
@ -6,15 +6,17 @@ const immutext = require('@src/immutext/');
|
||||
const initTs = require('./initTs');
|
||||
|
||||
module.exports = class {
|
||||
constructor(ts) {
|
||||
constructor(ts, immucfg) {
|
||||
this.startTime = new Date().getTime();
|
||||
this.$_ts = initTs(ts);
|
||||
this.$_ts = initTs(ts, immucfg);
|
||||
this.scd = getScd(this.$_ts.nsd);
|
||||
this.keynames = this.$_ts.cp[1];
|
||||
this.keycodes = []
|
||||
this.optext = globaltext();
|
||||
this.opmate = this.mateOper();
|
||||
this.opdata = dataOper();
|
||||
this.r2mkaText = null;
|
||||
this.immucfg = immucfg || immutext;
|
||||
}
|
||||
|
||||
run() {
|
||||
@ -43,7 +45,7 @@ module.exports = class {
|
||||
|
||||
parseGlobalText2() {
|
||||
const { opmate, opdata, optext, keynames, getCurr } = this;
|
||||
optext.init(0, immutext.globalText2);
|
||||
optext.init(0, this.immucfg.globalText2);
|
||||
opdata.init();
|
||||
opmate.init();
|
||||
opmate.setMate('G_$ht', true);
|
||||
@ -62,7 +64,7 @@ module.exports = class {
|
||||
|
||||
parseGlobalText1(codeArr = []) {
|
||||
const { opmate, opdata, optext, keynames, getCurr } = this;
|
||||
optext.init(0, immutext.globalText1);
|
||||
optext.init(0, this.immucfg.globalText1);
|
||||
opdata.init({ arr8: [4, 16, 64, 256, 1024, 4096, 16384, 65536] });
|
||||
opmate.init();
|
||||
opmate.setMate('G_$e4', true);
|
||||
@ -73,7 +75,8 @@ module.exports = class {
|
||||
opmate.setMate();
|
||||
this.keycodes.push(...optext.getLine(optext.getCode() * 55295 + optext.getCode()).split(String.fromCharCode(257)));
|
||||
opmate.setMate();
|
||||
this.keycodes.push(optext.getLine(optext.getCode() * 55295 + optext.getCode()));
|
||||
this.r2mkaText = optext.getLine(optext.getCode() * 55295 + optext.getCode())
|
||||
this.keycodes.push(this.r2mkaText);
|
||||
opmate.setMate('G_$gG', true);
|
||||
for (let i = 0; i < opmate.getMateOri('G_$gG'); i++) {
|
||||
this.gren(i, codeArr);
|
||||
|
@ -27,8 +27,8 @@ const {
|
||||
} = parser;
|
||||
|
||||
module.exports = class {
|
||||
constructor(ts) {
|
||||
parser.init(ts)
|
||||
constructor(ts, r2mkaText) {
|
||||
parser.init(ts, r2mkaText)
|
||||
this.config = {
|
||||
'window.navigator.maxTouchPoints': 0,
|
||||
'window.eval.toString().length': 33,
|
||||
|
@ -9,11 +9,11 @@ function grenJf () {
|
||||
return !flag;
|
||||
}
|
||||
|
||||
module.exports = function(defdata) {
|
||||
module.exports = function(defdata, immucfg = immutext.$_ts) {
|
||||
const cp = [];
|
||||
cp[0] = immutext.$_ts.cp0;
|
||||
cp[0] = immucfg.cp0;
|
||||
cp[1] = grenKeys(806, defdata.nsd);
|
||||
cp[2] = immutext.$_ts.cp2;
|
||||
cp[2] = immucfg.cp2;
|
||||
cp[6] = '';
|
||||
return {
|
||||
nsd: 0,
|
||||
|
@ -2,11 +2,11 @@ const gv = require('../globalVarible');
|
||||
const common = require('./common');
|
||||
const logger = require('@utils/logger');
|
||||
|
||||
function init(ts) {
|
||||
function init(ts, r2mkaText) {
|
||||
const startTime = new Date().getTime();
|
||||
gv.setAttr('utils', common);
|
||||
gv.setAttr('ts', ts);
|
||||
require('./r2mka').init();
|
||||
require('./r2mka').init(r2mkaText);
|
||||
require('./tscp').init();
|
||||
require('./tscd').init();
|
||||
require('./bignum').init();
|
||||
|
@ -84,7 +84,7 @@ exports.parse = function(str = gt3) {
|
||||
return parse(deepParse());
|
||||
};
|
||||
|
||||
exports.init = function() {
|
||||
gv.setAttr('r2mka', exports.parse(gt3));
|
||||
exports.init = function(r2mkaText) {
|
||||
gv.setAttr('r2mka', exports.parse(r2mkaText || gt3));
|
||||
logger.debug('头r2mka标识字符串完成解析!')
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ const paths = require('@utils/paths');
|
||||
const logger = require('@utils/logger');
|
||||
const fs = require('fs');
|
||||
|
||||
module.exports = function (ts) {
|
||||
module.exports = function (ts, immucfg) {
|
||||
const startTime = new Date().getTime();
|
||||
const coder = new Coder(ts);
|
||||
const coder = new Coder(ts, immucfg);
|
||||
const { code, $_ts } = coder.run();
|
||||
const files = [
|
||||
{
|
||||
|
@ -1,12 +1,20 @@
|
||||
const logger = require('@utils/logger');
|
||||
const Coder = require('./handler/Coder');
|
||||
const Cookie = require('./handler/Cookie');
|
||||
const unescape = require('@utils/unescape');
|
||||
|
||||
module.exports = function (ts) {
|
||||
function parseR2mka(text) {
|
||||
const start = text.indexOf('"') + 1;
|
||||
const end = text.lastIndexOf('"') - 2;
|
||||
return unescape(text.substr(start, end));
|
||||
}
|
||||
|
||||
module.exports = function (ts, immucfg) {
|
||||
const startTime = new Date().getTime();
|
||||
const coder = new Coder(ts);
|
||||
const coder = new Coder(ts, immucfg);
|
||||
const { code, $_ts } = coder.run();
|
||||
const cookie = new Cookie($_ts).run();
|
||||
const r2mkaText = parseR2mka(coder.r2mkaText);
|
||||
const cookie = new Cookie($_ts, r2mkaText).run();
|
||||
logger.info([`生成动态cookie成功!用时:${new Date().getTime() - startTime}ms\n`, `Cookie值: ${cookie}`, `Cookie长: ${cookie.length}\n`].join('\n '))
|
||||
}
|
||||
|
||||
|
12
utils/unescape.js
Normal file
12
utils/unescape.js
Normal file
@ -0,0 +1,12 @@
|
||||
// 转译还原
|
||||
module.exports = function(text) {
|
||||
try {
|
||||
try {
|
||||
return JSON.parse('"' + text + '"')
|
||||
} catch (e) {
|
||||
return eval('("' + text + '")')
|
||||
}
|
||||
} catch (e) {
|
||||
return text
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user