diff --git a/main.js b/main.js index ef37fb8..ea31a81 100755 --- a/main.js +++ b/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 diff --git a/src/adapt/cnipa/index.js b/src/adapt/cnipa/index.js new file mode 100644 index 0000000..ac98160 --- /dev/null +++ b/src/adapt/cnipa/index.js @@ -0,0 +1,6 @@ +module.exports = { + cp0: 'gl}y|sMnyn}', + cp2: 'g+*`-+`+0`.1`', + globalText1: "'r2mKa'.length", + globalText2: '&Ţfunction', +} diff --git a/src/adapt/index.js b/src/adapt/index.js new file mode 100644 index 0000000..0ae90dc --- /dev/null +++ b/src/adapt/index.js @@ -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 }; + }, {}); +} + diff --git a/src/adapt/readme.md b/src/adapt/readme.md new file mode 100644 index 0000000..e150d17 --- /dev/null +++ b/src/adapt/readme.md @@ -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', +} +``` + diff --git a/src/handler/Coder.js b/src/handler/Coder.js index a57c58a..c8b398b 100644 --- a/src/handler/Coder.js +++ b/src/handler/Coder.js @@ -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); diff --git a/src/handler/Cookie.js b/src/handler/Cookie.js index a625cc2..2cbdd8d 100644 --- a/src/handler/Cookie.js +++ b/src/handler/Cookie.js @@ -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, diff --git a/src/handler/initTs.js b/src/handler/initTs.js index 659fdb3..12f2fd3 100644 --- a/src/handler/initTs.js +++ b/src/handler/initTs.js @@ -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, diff --git a/src/handler/parser/index.js b/src/handler/parser/index.js index ac16d76..6570418 100644 --- a/src/handler/parser/index.js +++ b/src/handler/parser/index.js @@ -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(); diff --git a/src/handler/parser/r2mka.js b/src/handler/parser/r2mka.js index 0f40942..42509bf 100644 --- a/src/handler/parser/r2mka.js +++ b/src/handler/parser/r2mka.js @@ -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标识字符串完成解析!') } diff --git a/src/makeCode.js b/src/makeCode.js index 9d5eb7b..76fa42d 100644 --- a/src/makeCode.js +++ b/src/makeCode.js @@ -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 = [ { diff --git a/src/makeCookie.js b/src/makeCookie.js index 7fba565..c6acf9f 100644 --- a/src/makeCookie.js +++ b/src/makeCookie.js @@ -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 ')) } diff --git a/utils/unescape.js b/utils/unescape.js new file mode 100644 index 0000000..f9b2dee --- /dev/null +++ b/utils/unescape.js @@ -0,0 +1,12 @@ +// 转译还原 +module.exports = function(text) { + try { + try { + return JSON.parse('"' + text + '"') + } catch (e) { + return eval('("' + text + '")') + } + } catch (e) { + return text + } +}