From 1100a6d4f74bda7c749bfb949d7364cc67ddc7e6 Mon Sep 17 00:00:00 2001 From: smallfawn <860562056@qq.com> Date: Sat, 1 Mar 2025 17:06:50 +0800 Subject: [PATCH] update --- package.json | 1 + src/main.js | 93 ++++++++++++++++++++++++++-------------- src/plugin/awsc.js | 12 +++--- src/plugin/common.js | 20 +++++---- src/plugin/eval.js | 12 +++--- src/plugin/jjencode.js | 2 +- src/plugin/obfuscator.js | 49 +++++++++------------ src/plugin/sojson.js | 26 +++++------ src/plugin/sojsonv7.js | 43 +++++++++++++------ 9 files changed, 151 insertions(+), 107 deletions(-) diff --git a/package.json b/package.json index c7ab5d3..5efae18 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "decode-js", + "type": "module", "scripts": { "decode": "node src/main.js", "deob": "node src/main.js -t obfuscator", diff --git a/src/main.js b/src/main.js index 8400646..4b9e752 100644 --- a/src/main.js +++ b/src/main.js @@ -1,46 +1,62 @@ -const fs = require('fs') -const PluginCommon = require('./plugin/common.js') -const PluginJjencode = require('./plugin/jjencode.js') -const PluginSojson = require('./plugin/sojson.js') -const PluginSojsonV7 = require('./plugin/sojsonv7.js') -const PluginObfuscator = require('./plugin/obfuscator.js') -const PluginAwsc = require('./plugin/awsc.js') +import fs from 'fs'; +import { fileURLToPath } from 'url'; +import * as path from 'path'; +import process from 'process'; + +// Dynamically import ESM modules +const commonModule = await import('./plugin/common.js'); +const jjencodeModule = await import('./plugin/jjencode.js'); +const sojsonModule = await import('./plugin/sojson.js'); +const sojsonv7Module = await import('./plugin/sojsonv7.js'); +const obfuscatorModule = await import('./plugin/obfuscator.js'); +const awscModule = await import('./plugin/awsc.js'); + +// Provide default exports if necessary +const PluginCommon = commonModule.default || commonModule; +const PluginJjencode = jjencodeModule.default || jjencodeModule; +const PluginSojson = sojsonModule.default || sojsonModule; +const PluginSojsonV7 = sojsonv7Module.default || sojsonv7Module; +const PluginObfuscator = obfuscatorModule.default || obfuscatorModule; +const PluginAwsc = awscModule.default || awscModule; + +// Read command-line arguments +let encodeFile = 'input.js'; +let decodeFile = 'output.js'; -// 读取参数 -let encodeFile = 'input.js' -let decodeFile = 'output.js' for (let i = 2; i < process.argv.length; i += 2) { if (process.argv[i] === '-i') { - encodeFile = process.argv[i + 1] - } - if (process.argv[i] === '-o') { - decodeFile = process.argv[i + 1] + encodeFile = process.argv[i + 1]; + } else if (process.argv[i] === '-o') { + decodeFile = process.argv[i + 1]; } } -console.log(`输入: ${encodeFile}`) -console.log(`输出: ${decodeFile}`) -// 读取源代码 -const sourceCode = fs.readFileSync(encodeFile, { encoding: 'utf-8' }) +console.log(`输入: ${encodeFile}`); +console.log(`输出: ${decodeFile}`); + +// Read source code +const sourceCode = fs.readFileSync(encodeFile, { encoding: 'utf-8' }); let processedCode = sourceCode; let pluginUsed = ''; +let time; -// 循环尝试不同的插件,直到源代码与处理后的代码不一致 +// Try plugins in sequence until the processed code differs from the original const plugins = [ { name: 'obfuscator', plugin: PluginObfuscator }, { name: 'sojsonv7', plugin: PluginSojsonV7 }, { name: 'sojson', plugin: PluginSojson }, - { name: 'awsc', plugin: PluginAwsc }, { name: 'jjencode', plugin: PluginJjencode }, - { name: 'common', plugin: PluginCommon },// 最后一次使用通用插件 + { name: 'common', plugin: PluginCommon }, // Use common plugin last ]; -for (let plugin of plugins) { - if (sourceCode.indexOf("smEcV") != -1) { - break +for (const plugin of plugins) { + // Check for specific string in sourceCode to break early + if (sourceCode.indexOf('smEcV') !== -1) { + break; } + try { const code = plugin.plugin(sourceCode); if (code && code !== processedCode) { @@ -50,18 +66,29 @@ for (let plugin of plugins) { } } catch (error) { console.error(`插件 ${plugin.name} 处理时发生错误: ${error.message}`); - // 继续循环尝试下一个插件 - continue; } } -let time = new Date(); + +// Check if processed code differs from source code if (processedCode !== sourceCode) { - // 输出代码 - fs.writeFile(decodeFile, "//" + time + '\n' + "//Base:https://github.com/echo094/decode-js" + '\n' + "//Modify:https://github.com/smallfawn/decode_action" + '\n' + processedCode, (err) => { - if (err) throw err; - console.log(`使用插件 ${pluginUsed} 成功处理并写入文件 ${decodeFile}`); + time = new Date(); + const header = [ + `//${time}`, + "//Base:https://github.com/echo094/decode-js", + "//Modify:https://github.com/smallfawn/decode_action" + ].join('\n'); + + // Combine header and processed code + const outputCode = header + '\n' + processedCode; + + // Write to file + fs.writeFile(decodeFile, outputCode, (err) => { + if (err) { + throw err; + } else { + console.log(`使用插件 ${pluginUsed} 成功处理并写入文件 ${decodeFile}`); + } }); } else { console.log(`所有插件处理后的代码与原代码一致,未写入文件。`); -} - +} \ No newline at end of file diff --git a/src/plugin/awsc.js b/src/plugin/awsc.js index cbb9519..fe56bb7 100644 --- a/src/plugin/awsc.js +++ b/src/plugin/awsc.js @@ -2,10 +2,12 @@ * Reference: * * [某宝登录bx-ua参数逆向思路(fireyejs 225算法)](https://zhuanlan.zhihu.com/p/626187669) */ -const { parse } = require('@babel/parser') -const generator = require('@babel/generator').default -const traverse = require('@babel/traverse').default -const t = require('@babel/types') +import { parse } from '@babel/parser' +import _generate from '@babel/generator' +const generator = _generate.default +import _traverse from '@babel/traverse' +const traverse = _traverse.default +import * as t from '@babel/types' function RemoveVoid(path) { if (path.node.operator === 'void') { @@ -204,7 +206,7 @@ function LintBlock(path) { path.replaceWith(t.blockStatement(arr)) } -module.exports = function (code) { +export default function (code) { let ast = parse(code) // Lint traverse(ast, { diff --git a/src/plugin/common.js b/src/plugin/common.js index 471657b..e0674e8 100644 --- a/src/plugin/common.js +++ b/src/plugin/common.js @@ -1,8 +1,14 @@ -const { parse } = require('@babel/parser') -const generator = require('@babel/generator').default -const traverse = require('@babel/traverse').default +import { parse } from '@babel/parser' +import _generate from '@babel/generator' +const generator = _generate.default +import _traverse from '@babel/traverse' +const traverse = _traverse.default +import deleteUnreachableCode from '../visitor/delete-unreachable-code.js' +import deleteNestedBlocks from '../visitor/delete-nested-blocks.js' +import calculateConstantExp from '../visitor/calculate-constant-exp.js' +import calculateRString from '../visitor/calculate-rstring.js' -module.exports = function (code) { +export default function (code) { let ast try { ast = parse(code, { errorRecovery: true }) @@ -10,11 +16,9 @@ module.exports = function (code) { console.error(`Cannot parse code: ${e.reasonCode}`) return null } - const deleteExtra = require('../visitor/delete-extra') - traverse(ast, deleteExtra) - const calculateConstantExp = require('../visitor/calculate-constant-exp') + traverse(ast, deleteUnreachableCode) + traverse(ast, deleteNestedBlocks) traverse(ast, calculateConstantExp) - const calculateRString = require('../visitor/calculate-rstring') traverse(ast, calculateRString) code = generator(ast).code return code diff --git a/src/plugin/eval.js b/src/plugin/eval.js index 142638f..a1c49eb 100644 --- a/src/plugin/eval.js +++ b/src/plugin/eval.js @@ -1,7 +1,9 @@ -const { parse } = require('@babel/parser') -const generator = require('@babel/generator').default -const traverse = require('@babel/traverse').default -const t = require('@babel/types') +import { parse } from '@babel/parser' +import _generate from '@babel/generator' +const generator = _generate.default +import _traverse from '@babel/traverse' +const traverse = _traverse.default +import * as t from '@babel/types' function unpack(code) { let ast = parse(code, { errorRecovery: true }) @@ -46,7 +48,7 @@ function pack(code) { return code } -module.exports = { +export default { unpack, pack, } diff --git a/src/plugin/jjencode.js b/src/plugin/jjencode.js index af14f8c..2a79fc0 100644 --- a/src/plugin/jjencode.js +++ b/src/plugin/jjencode.js @@ -57,7 +57,7 @@ function getCode(code) { * This encoding method originates from http://utf-8.jp/public/jjencode.html, * and it does not change the original code (encoder, not obfuscation). */ -module.exports = function (code) { +export default function (code) { code = getCode(code) if (!code) { return null diff --git a/src/plugin/obfuscator.js b/src/plugin/obfuscator.js index 44131b6..59cee75 100644 --- a/src/plugin/obfuscator.js +++ b/src/plugin/obfuscator.js @@ -3,13 +3,24 @@ * * cilame/v_jstools * * Cqxstevexw/decodeObfuscator */ -const { parse } = require('@babel/parser') -const generator = require('@babel/generator').default -const traverse = require('@babel/traverse').default -const t = require('@babel/types') -const ivm = require('isolated-vm') -const PluginEval = require('./eval.js') -const calculateConstantExp = require('../visitor/calculate-constant-exp') +import { parse } from '@babel/parser' +import _generate from '@babel/generator' +const generator = _generate.default +import _traverse from '@babel/traverse' +const traverse = _traverse.default +import * as t from '@babel/types' +import ivm from 'isolated-vm' +import PluginEval from './eval.js' +import calculateConstantExp from '../visitor/calculate-constant-exp.js' +import deleteIllegalReturn from '../visitor/delete-illegal-return.js' +import deleteUnusedVar from '../visitor/delete-unused-var.js' +import lintIfStatement from '../visitor/lint-if-statement.js' +import mergeObject from '../visitor/merge-object.js' +import parseControlFlowStorage from '../visitor/parse-control-flow-storage.js' +import pruneIfBranch from '../visitor/prune-if-branch.js' +import splitAssignment from '../visitor/split-assignment.js' +import splitSequence from '../visitor/split-sequence.js' +import splitVarDeclaration from '../visitor/split-variable-declaration.js' const isolate = new ivm.Isolate() const globalContext = isolate.createContextSync() @@ -609,10 +620,8 @@ function decodeCodeBlock(ast) { // 合并字面量 traverse(ast, calculateConstantExp) // 先合并分离的Object定义 - const mergeObject = require('../visitor/merge-object') traverse(ast, mergeObject) // 在变量定义完成后判断是否为代码块加密内容 - const parseControlFlowStorage = require('../visitor/parse-control-flow-storage') traverse(ast, parseControlFlowStorage) // 合并字面量(在解除区域混淆后会出现新的可合并分割) traverse(ast, calculateConstantExp) @@ -719,24 +728,11 @@ function cleanSwitchCode(path) { function cleanDeadCode(ast) { traverse(ast, calculateConstantExp) - const pruneIfBranch = require('../visitor/prune-if-branch') traverse(ast, pruneIfBranch) traverse(ast, { WhileStatement: { exit: cleanSwitchCode } }) return ast } -const splitVariableDeclarator = { - VariableDeclarator(path) { - const init = path.get('init') - if (!init.isAssignmentExpression()) { - return - } - path.parentPath.insertBefore(init.node) - init.replaceWith(init.node.left) - path.parentPath.scope.crawl() - }, -} - function standardIfStatement(path) { const consequent = path.get('consequent') const alternate = path.get('alternate') @@ -795,9 +791,8 @@ function purifyCode(ast) { path.remove() }, }) + traverse(ast, splitAssignment) // 删除未使用的变量 - traverse(ast, splitVariableDeclarator) - const deleteUnusedVar = require('../visitor/delete-unused-var') traverse(ast, deleteUnusedVar) // 替换索引器 function FormatMember(path) { @@ -844,7 +839,6 @@ function purifyCode(ast) { }) // 拆分语句 - const splitSequence = require('../visitor/split-sequence') traverse(ast, splitSequence) return ast } @@ -1041,7 +1035,7 @@ function unlockEnv(ast) { return ast } -module.exports = function (code) { +export default function (code) { let ret = PluginEval.unpack(code) let global_eval = false if (ret) { @@ -1056,13 +1050,10 @@ module.exports = function (code) { return null } // IllegalReturn - const deleteIllegalReturn = require('../visitor/delete-illegal-return') traverse(ast, deleteIllegalReturn) // Lint before split statements - const lintIfStatement = require('../visitor/lint-if-statement') traverse(ast, lintIfStatement) // Split declarations to avoid bugs - const splitVarDeclaration = require('../visitor/split-variable-declaration') traverse(ast, splitVarDeclaration) // 清理二进制显示内容 traverse(ast, { diff --git a/src/plugin/sojson.js b/src/plugin/sojson.js index 9babebc..fdfc0eb 100644 --- a/src/plugin/sojson.js +++ b/src/plugin/sojson.js @@ -1,13 +1,19 @@ /** * 在 babel_asttool.js 的基础上修改而来 */ -const { parse } = require('@babel/parser') -const generator = require('@babel/generator').default -const traverse = require('@babel/traverse').default -const t = require('@babel/types') -const ivm = require('isolated-vm') -const PluginEval = require('./eval.js') -const calculateConstantExp = require('../visitor/calculate-constant-exp') +import { parse } from '@babel/parser' +import _generate from '@babel/generator' +const generator = _generate.default +import _traverse from '@babel/traverse' +const traverse = _traverse.default +import * as t from '@babel/types' +import ivm from 'isolated-vm' +import PluginEval from './eval.js' +import calculateConstantExp from '../visitor/calculate-constant-exp.js' +import deleteUnusedVar from '../visitor/delete-unused-var.js' +import parseControlFlowStorage from '../visitor/parse-control-flow-storage.js' +import pruneIfBranch from '../visitor/prune-if-branch.js' +import splitSequence from '../visitor/split-sequence.js' const isolate = new ivm.Isolate() const globalContext = isolate.createContextSync() @@ -166,7 +172,6 @@ function cleanSwitchCode(path) { function cleanDeadCode(ast) { traverse(ast, calculateConstantExp) - const pruneIfBranch = require('../visitor/prune-if-branch') traverse(ast, pruneIfBranch) traverse(ast, { WhileStatement: { exit: cleanSwitchCode } }) return ast @@ -469,7 +474,6 @@ function purifyCode(ast) { } traverse(ast, { MemberExpression: FormatMember }) // 分割表达式 - const splitSequence = require('../visitor/split-sequence') traverse(ast, splitSequence) // 删除空语句 traverse(ast, { @@ -478,12 +482,11 @@ function purifyCode(ast) { }, }) // 删除未使用的变量 - const deleteUnusedVar = require('../visitor/delete-unused-var') traverse(ast, deleteUnusedVar) return ast } -module.exports = function (code) { +export default function (code) { let ret = PluginEval.unpack(code) let global_eval = false if (ret) { @@ -508,7 +511,6 @@ module.exports = function (code) { return null } console.log('处理代码块加密...') - const parseControlFlowStorage = require('../visitor/parse-control-flow-storage') traverse(ast, parseControlFlowStorage) console.log('清理死代码...') ast = cleanDeadCode(ast) diff --git a/src/plugin/sojsonv7.js b/src/plugin/sojsonv7.js index 6a1013e..26a476f 100644 --- a/src/plugin/sojsonv7.js +++ b/src/plugin/sojsonv7.js @@ -1,13 +1,20 @@ /** * For jsjiami.com.v7 */ -const { parse } = require('@babel/parser') -const generator = require('@babel/generator').default -const traverse = require('@babel/traverse').default -const t = require('@babel/types') -const ivm = require('isolated-vm') -const PluginEval = require('./eval.js') -const calculateConstantExp = require('../visitor/calculate-constant-exp') +import { parse } from '@babel/parser' +import _generate from '@babel/generator' +const generator = _generate.default +import _traverse from '@babel/traverse' +const traverse = _traverse.default +import * as t from '@babel/types' +import ivm from 'isolated-vm' +import PluginEval from './eval.js' +import calculateConstantExp from '../visitor/calculate-constant-exp.js' +import deleteIllegalReturn from '../visitor/delete-illegal-return.js' +import deleteUnusedVar from '../visitor/delete-unused-var.js' +import parseControlFlowStorage from '../visitor/parse-control-flow-storage.js' +import pruneIfBranch from '../visitor/prune-if-branch.js' +import splitSequence from '../visitor/split-sequence.js' const isolate = new ivm.Isolate() const globalContext = isolate.createContextSync() @@ -283,7 +290,11 @@ function decodeGlobal(ast) { if (item.path.isFunctionDeclaration()) { scope = item.path.parentPath.scope } - const refs = scope.bindings[cur_val].referencePaths + // var is function scoped and let is block scoped + // Hence, var may not be in the current scope, e.g., in a for-loop + const binding = scope.getBinding(cur_val) + scope = binding.scope + const refs = binding.referencePaths const refs_next = [] for (let ref of refs) { const parent = ref.parentPath @@ -294,12 +305,21 @@ function decodeGlobal(ast) { path: parent, code: 'var ' + parent, }) + } else if (ref.key === 'right') { + // AssignmentExpression + refs_next.push({ + name: parent.node.left.name, + path: parent, + code: 'var ' + parent, + }) } else if (ref.key === 'object') { // MemberExpression memToStr(parent) } else if (ref.key === 'callee') { // CallExpression funToStr(parent) + } else { + console.error('Unexpected reference') } } for (let ref of refs_next) { @@ -477,7 +497,6 @@ function cleanSwitchCode2(path) { function cleanDeadCode(ast) { traverse(ast, calculateConstantExp) - const pruneIfBranch = require('../visitor/prune-if-branch') traverse(ast, pruneIfBranch) traverse(ast, { WhileStatement: { exit: cleanSwitchCode1 } }) traverse(ast, { ForStatement: { exit: cleanSwitchCode2 } }) @@ -722,7 +741,6 @@ function purifyCode(ast) { } traverse(ast, { MemberExpression: FormatMember }) // 分割表达式 - const splitSequence = require('../visitor/split-sequence') traverse(ast, splitSequence) // 删除空语句 traverse(ast, { @@ -731,11 +749,10 @@ function purifyCode(ast) { }, }) // 删除未使用的变量 - const deleteUnusedVar = require('../visitor/delete-unused-var') traverse(ast, deleteUnusedVar) } -module.exports = function (code) { +export default function (code) { let ret = PluginEval.unpack(code) let global_eval = false if (ret) { @@ -750,7 +767,6 @@ module.exports = function (code) { return null } // IllegalReturn - const deleteIllegalReturn = require('../visitor/delete-illegal-return') traverse(ast, deleteIllegalReturn) // 清理二进制显示内容 traverse(ast, { @@ -769,7 +785,6 @@ module.exports = function (code) { return null } console.log('处理代码块加密...') - const parseControlFlowStorage = require('../visitor/parse-control-flow-storage') traverse(ast, parseControlFlowStorage) console.log('清理死代码...') ast = cleanDeadCode(ast)