mirror of
https://github.com/pysunday/rs-reverse.git
synced 2025-04-19 03:39:43 +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 request = require('request-promise');
|
||||||
const cheerio = require('cheerio');
|
const cheerio = require('cheerio');
|
||||||
const log4js = require('log4js');
|
const log4js = require('log4js');
|
||||||
|
const urlresolve = require('url').resolve;
|
||||||
|
const adapt = require('@src/adapt');
|
||||||
|
|
||||||
function debugLog(level) {
|
function debugLog(level) {
|
||||||
if (level) {
|
if (level) {
|
||||||
@ -25,6 +27,24 @@ function debugLog(level) {
|
|||||||
logger.trace('paths:\n', JSON.stringify(paths, null, 2));
|
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 = {
|
const commandBuilder = {
|
||||||
f: {
|
f: {
|
||||||
alias: 'file',
|
alias: 'file',
|
||||||
@ -45,28 +65,25 @@ const commandBuilder = {
|
|||||||
alias: 'url',
|
alias: 'url',
|
||||||
describe: '瑞数返回204状态码的请求地址',
|
describe: '瑞数返回204状态码的请求地址',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
coerce: async (input) => {
|
coerce: getCode,
|
||||||
if (!isValidUrl(input)) throw new Error('输入链接不正确');
|
},
|
||||||
try {
|
a: {
|
||||||
const res = await request(input)
|
alias: 'adapt',
|
||||||
const $ = cheerio.load(res);
|
describe: '已经做了适配的网站名称,不传则为cnipa',
|
||||||
const scripts = [...$('script')].map(ele => $(ele).text())
|
type: 'string',
|
||||||
.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('输入链接无效');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const commandHandler = (command, argv) => {
|
const commandHandler = (command, argv) => {
|
||||||
debugLog(argv.level);
|
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.nsd: ${ts.nsd}`);
|
||||||
logger.trace(`传入的$_ts.cd: ${ts.cd}`);
|
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
|
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');
|
const initTs = require('./initTs');
|
||||||
|
|
||||||
module.exports = class {
|
module.exports = class {
|
||||||
constructor(ts) {
|
constructor(ts, immucfg) {
|
||||||
this.startTime = new Date().getTime();
|
this.startTime = new Date().getTime();
|
||||||
this.$_ts = initTs(ts);
|
this.$_ts = initTs(ts, immucfg);
|
||||||
this.scd = getScd(this.$_ts.nsd);
|
this.scd = getScd(this.$_ts.nsd);
|
||||||
this.keynames = this.$_ts.cp[1];
|
this.keynames = this.$_ts.cp[1];
|
||||||
this.keycodes = []
|
this.keycodes = []
|
||||||
this.optext = globaltext();
|
this.optext = globaltext();
|
||||||
this.opmate = this.mateOper();
|
this.opmate = this.mateOper();
|
||||||
this.opdata = dataOper();
|
this.opdata = dataOper();
|
||||||
|
this.r2mkaText = null;
|
||||||
|
this.immucfg = immucfg || immutext;
|
||||||
}
|
}
|
||||||
|
|
||||||
run() {
|
run() {
|
||||||
@ -43,7 +45,7 @@ module.exports = class {
|
|||||||
|
|
||||||
parseGlobalText2() {
|
parseGlobalText2() {
|
||||||
const { opmate, opdata, optext, keynames, getCurr } = this;
|
const { opmate, opdata, optext, keynames, getCurr } = this;
|
||||||
optext.init(0, immutext.globalText2);
|
optext.init(0, this.immucfg.globalText2);
|
||||||
opdata.init();
|
opdata.init();
|
||||||
opmate.init();
|
opmate.init();
|
||||||
opmate.setMate('G_$ht', true);
|
opmate.setMate('G_$ht', true);
|
||||||
@ -62,7 +64,7 @@ module.exports = class {
|
|||||||
|
|
||||||
parseGlobalText1(codeArr = []) {
|
parseGlobalText1(codeArr = []) {
|
||||||
const { opmate, opdata, optext, keynames, getCurr } = this;
|
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] });
|
opdata.init({ arr8: [4, 16, 64, 256, 1024, 4096, 16384, 65536] });
|
||||||
opmate.init();
|
opmate.init();
|
||||||
opmate.setMate('G_$e4', true);
|
opmate.setMate('G_$e4', true);
|
||||||
@ -73,7 +75,8 @@ module.exports = class {
|
|||||||
opmate.setMate();
|
opmate.setMate();
|
||||||
this.keycodes.push(...optext.getLine(optext.getCode() * 55295 + optext.getCode()).split(String.fromCharCode(257)));
|
this.keycodes.push(...optext.getLine(optext.getCode() * 55295 + optext.getCode()).split(String.fromCharCode(257)));
|
||||||
opmate.setMate();
|
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);
|
opmate.setMate('G_$gG', true);
|
||||||
for (let i = 0; i < opmate.getMateOri('G_$gG'); i++) {
|
for (let i = 0; i < opmate.getMateOri('G_$gG'); i++) {
|
||||||
this.gren(i, codeArr);
|
this.gren(i, codeArr);
|
||||||
|
@ -27,8 +27,8 @@ const {
|
|||||||
} = parser;
|
} = parser;
|
||||||
|
|
||||||
module.exports = class {
|
module.exports = class {
|
||||||
constructor(ts) {
|
constructor(ts, r2mkaText) {
|
||||||
parser.init(ts)
|
parser.init(ts, r2mkaText)
|
||||||
this.config = {
|
this.config = {
|
||||||
'window.navigator.maxTouchPoints': 0,
|
'window.navigator.maxTouchPoints': 0,
|
||||||
'window.eval.toString().length': 33,
|
'window.eval.toString().length': 33,
|
||||||
|
@ -9,11 +9,11 @@ function grenJf () {
|
|||||||
return !flag;
|
return !flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function(defdata) {
|
module.exports = function(defdata, immucfg = immutext.$_ts) {
|
||||||
const cp = [];
|
const cp = [];
|
||||||
cp[0] = immutext.$_ts.cp0;
|
cp[0] = immucfg.cp0;
|
||||||
cp[1] = grenKeys(806, defdata.nsd);
|
cp[1] = grenKeys(806, defdata.nsd);
|
||||||
cp[2] = immutext.$_ts.cp2;
|
cp[2] = immucfg.cp2;
|
||||||
cp[6] = '';
|
cp[6] = '';
|
||||||
return {
|
return {
|
||||||
nsd: 0,
|
nsd: 0,
|
||||||
|
@ -2,11 +2,11 @@ const gv = require('../globalVarible');
|
|||||||
const common = require('./common');
|
const common = require('./common');
|
||||||
const logger = require('@utils/logger');
|
const logger = require('@utils/logger');
|
||||||
|
|
||||||
function init(ts) {
|
function init(ts, r2mkaText) {
|
||||||
const startTime = new Date().getTime();
|
const startTime = new Date().getTime();
|
||||||
gv.setAttr('utils', common);
|
gv.setAttr('utils', common);
|
||||||
gv.setAttr('ts', ts);
|
gv.setAttr('ts', ts);
|
||||||
require('./r2mka').init();
|
require('./r2mka').init(r2mkaText);
|
||||||
require('./tscp').init();
|
require('./tscp').init();
|
||||||
require('./tscd').init();
|
require('./tscd').init();
|
||||||
require('./bignum').init();
|
require('./bignum').init();
|
||||||
|
@ -84,7 +84,7 @@ exports.parse = function(str = gt3) {
|
|||||||
return parse(deepParse());
|
return parse(deepParse());
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.init = function() {
|
exports.init = function(r2mkaText) {
|
||||||
gv.setAttr('r2mka', exports.parse(gt3));
|
gv.setAttr('r2mka', exports.parse(r2mkaText || gt3));
|
||||||
logger.debug('头r2mka标识字符串完成解析!')
|
logger.debug('头r2mka标识字符串完成解析!')
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ const paths = require('@utils/paths');
|
|||||||
const logger = require('@utils/logger');
|
const logger = require('@utils/logger');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
module.exports = function (ts) {
|
module.exports = function (ts, immucfg) {
|
||||||
const startTime = new Date().getTime();
|
const startTime = new Date().getTime();
|
||||||
const coder = new Coder(ts);
|
const coder = new Coder(ts, immucfg);
|
||||||
const { code, $_ts } = coder.run();
|
const { code, $_ts } = coder.run();
|
||||||
const files = [
|
const files = [
|
||||||
{
|
{
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
const logger = require('@utils/logger');
|
const logger = require('@utils/logger');
|
||||||
const Coder = require('./handler/Coder');
|
const Coder = require('./handler/Coder');
|
||||||
const Cookie = require('./handler/Cookie');
|
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 startTime = new Date().getTime();
|
||||||
const coder = new Coder(ts);
|
const coder = new Coder(ts, immucfg);
|
||||||
const { code, $_ts } = coder.run();
|
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 '))
|
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