js-deobfuscator
Advanced tools
Comparing version
@@ -14,3 +14,3 @@ #!/usr/bin/env node | ||
.option('-o, --output [output_file]', 'The deobfuscated output file', 'output/output.js') | ||
.option('-m, --module', 'Parse ESModule'); | ||
.option('-m, --module', 'Parse an ESModule'); | ||
commander_1.program.parse(process.argv); | ||
@@ -24,5 +24,5 @@ const options = commander_1.program.opts(); | ||
const source = fs_1.default.readFileSync(options.input).toString(); | ||
const config = options.isModule ? { isModule: true } : {}; | ||
const config = options.module ? { isModule: true } : {}; | ||
const output = (0, index_1.deobfuscate)(source, config); | ||
fs_1.default.writeFileSync(options.output, output); | ||
console.info(`The output file ${options.output} has been created`); |
@@ -7,3 +7,3 @@ import * as Shift from 'shift-ast'; | ||
*/ | ||
static cleanup(ast: Shift.Script): void; | ||
static cleanup(ast: Shift.Script | Shift.Module): void; | ||
/** | ||
@@ -10,0 +10,0 @@ * Returns whether a node is a variable declaration statement with no |
import * as Shift from 'shift-ast'; | ||
export default abstract class Modification { | ||
name: string; | ||
ast: Shift.Script; | ||
ast: Shift.Script | Shift.Module; | ||
/** | ||
@@ -10,3 +10,3 @@ * Creates a new modification. | ||
*/ | ||
constructor(name: string, ast: Shift.Script); | ||
constructor(name: string, ast: Shift.Script | Shift.Module); | ||
/** | ||
@@ -13,0 +13,0 @@ * Executes the modification. |
@@ -13,3 +13,3 @@ import Modification from '../../modification'; | ||
*/ | ||
constructor(ast: Shift.Script, removeArrays: boolean); | ||
constructor(ast: Shift.Script | Shift.Module, removeArrays: boolean); | ||
/** | ||
@@ -16,0 +16,0 @@ * Executes the modification. |
@@ -8,3 +8,3 @@ import * as Shift from 'shift-ast'; | ||
*/ | ||
constructor(ast: Shift.Script); | ||
constructor(ast: Shift.Script | Shift.Module); | ||
/** | ||
@@ -11,0 +11,0 @@ * Executes the modification. |
@@ -11,3 +11,3 @@ import Modification from '../../modification'; | ||
*/ | ||
constructor(ast: Shift.Script); | ||
constructor(ast: Shift.Script | Shift.Module); | ||
/** | ||
@@ -14,0 +14,0 @@ * Executes the modification. |
import Modification from '../../modification'; | ||
import * as Shift from 'shift-ast'; | ||
/** | ||
* This transformation recovers strings which have obfuscated via various techniques. | ||
* | ||
* Reversed strings: | ||
* "dlroW olleH".split("").reverse().join("") -> "Hello World" | ||
* | ||
* Strings split into char codes: | ||
* String.fromCharCode(72,101,108,108,111,32,87,111,114,108,100) -> "Hello World" | ||
*/ | ||
export default class StringDecoder extends Modification { | ||
private encodingFunctionProps?; | ||
/** | ||
@@ -17,3 +9,3 @@ * Creates a new modification. | ||
*/ | ||
constructor(ast: Shift.Script); | ||
constructor(ast: Shift.Script | Shift.Module); | ||
/** | ||
@@ -24,2 +16,6 @@ * Executes the modification. | ||
/** | ||
* Looks for a JS-Obfuscator XOR string encoding function. | ||
*/ | ||
private findStringEncodingFunction; | ||
/** | ||
* Undoes various string operations. | ||
@@ -40,2 +36,14 @@ */ | ||
private isCharCodesString; | ||
/** | ||
* Returns whether a node is the JS-Obfuscator XOR string encoding function. | ||
* @param node The AST node. | ||
* @returns Whether. | ||
*/ | ||
private isXorStringEncodingFunction; | ||
/** | ||
* Returns whether a node is a call of the string encoding function. | ||
* @param node The AST node. | ||
* @returns Whether. | ||
*/ | ||
private isStringEncodingFunctionCall; | ||
} |
@@ -33,11 +33,2 @@ "use strict"; | ||
const traversalHelper_1 = __importDefault(require("../../helpers/traversalHelper")); | ||
/** | ||
* This transformation recovers strings which have obfuscated via various techniques. | ||
* | ||
* Reversed strings: | ||
* "dlroW olleH".split("").reverse().join("") -> "Hello World" | ||
* | ||
* Strings split into char codes: | ||
* String.fromCharCode(72,101,108,108,111,32,87,111,114,108,100) -> "Hello World" | ||
*/ | ||
class StringDecoder extends modification_1.default { | ||
@@ -55,5 +46,23 @@ /** | ||
execute() { | ||
this.findStringEncodingFunction(); | ||
this.undoStringOperations(); | ||
} | ||
/** | ||
* Looks for a JS-Obfuscator XOR string encoding function. | ||
*/ | ||
findStringEncodingFunction() { | ||
const self = this; | ||
// only look at top level | ||
// TODO: once scope system has been improved, rework this | ||
const statements = this.ast.type === 'Script' ? this.ast.statements : this.ast.items; | ||
for (const statement of statements) { | ||
if (self.isXorStringEncodingFunction(statement)) { | ||
const name = statement.name.name; | ||
const key = statement.body.statements[0].expression.expression.value; | ||
this.encodingFunctionProps = { name, key }; | ||
traversalHelper_1.default.removeNode(this.ast, statement); | ||
} | ||
} | ||
} | ||
/** | ||
* Undoes various string operations. | ||
@@ -93,2 +102,16 @@ */ | ||
} | ||
else if (self.encodingFunctionProps != undefined && | ||
self.isStringEncodingFunctionCall(node)) { | ||
/** | ||
* Handles strings obfuscated with JS-Obfuscator XOR string encoding. | ||
*/ | ||
const arg = node.arguments[0].value; | ||
const decodedStr = arg | ||
.split('.') | ||
.slice(0, -1) | ||
.map(c => String.fromCharCode(parseInt(c) ^ self.encodingFunctionProps.key)) | ||
.join(''); | ||
const replacement = new Shift.LiteralStringExpression({ value: decodedStr }); | ||
traversalHelper_1.default.replaceNode(parent, node, replacement); | ||
} | ||
} | ||
@@ -135,3 +158,44 @@ }); | ||
} | ||
/** | ||
* Returns whether a node is the JS-Obfuscator XOR string encoding function. | ||
* @param node The AST node. | ||
* @returns Whether. | ||
*/ | ||
isXorStringEncodingFunction(node) { | ||
var _a, _b, _c, _d; | ||
return (node.type === 'FunctionDeclaration' && | ||
node.params.items.length === 2 && | ||
node.params.rest == undefined && | ||
node.body.statements.length === 5 && | ||
node.body.statements[0].type === 'ExpressionStatement' && | ||
node.body.statements[0].expression.type === 'AssignmentExpression' && | ||
node.body.statements[0].expression.binding.type === 'AssignmentTargetIdentifier' && | ||
node.body.statements[0].expression.expression.type === 'LiteralNumericExpression' && | ||
node.body.statements[1].type === 'VariableDeclarationStatement' && | ||
node.body.statements[1].declaration.declarators.length === 3 && | ||
node.body.statements[2].type === 'ExpressionStatement' && | ||
node.body.statements[3].type === 'ForStatement' && | ||
((_a = node.body.statements[3].init) === null || _a === void 0 ? void 0 : _a.type) === 'AssignmentExpression' && | ||
((_b = node.body.statements[3].test) === null || _b === void 0 ? void 0 : _b.type) === 'BinaryExpression' && | ||
((_c = node.body.statements[3].update) === null || _c === void 0 ? void 0 : _c.type) === 'UpdateExpression' && | ||
node.body.statements[3].body.type === 'BlockStatement' && | ||
node.body.statements[4].type === 'ReturnStatement' && | ||
((_d = node.body.statements[4].expression) === null || _d === void 0 ? void 0 : _d.type) === 'IdentifierExpression'); | ||
} | ||
/** | ||
* Returns whether a node is a call of the string encoding function. | ||
* @param node The AST node. | ||
* @returns Whether. | ||
*/ | ||
isStringEncodingFunctionCall(node) { | ||
return (this.encodingFunctionProps != undefined && | ||
node.type === 'CallExpression' && | ||
node.callee.type === 'IdentifierExpression' && | ||
node.callee.name === this.encodingFunctionProps.name && | ||
node.arguments.length === 1 && | ||
node.arguments[0].type === 'LiteralStringExpression' && | ||
/^(\d{1,3}\.)+$/.test(node.arguments[0].value) // matches "65.108.101." etc | ||
); | ||
} | ||
} | ||
exports.default = StringDecoder; |
@@ -8,3 +8,3 @@ import Modification from '../../modification'; | ||
*/ | ||
constructor(ast: Shift.Script); | ||
constructor(ast: Shift.Script | Shift.Module); | ||
/** | ||
@@ -11,0 +11,0 @@ * Executes the modification. |
@@ -17,3 +17,3 @@ import Modification from '../../modification'; | ||
*/ | ||
constructor(ast: Shift.Script, removeProxyFunctions: boolean); | ||
constructor(ast: Shift.Script | Shift.Module, removeProxyFunctions: boolean); | ||
/** | ||
@@ -20,0 +20,0 @@ * Executes the modification. |
@@ -12,3 +12,3 @@ import Modification from '../../modification'; | ||
*/ | ||
constructor(ast: Shift.Script); | ||
constructor(ast: Shift.Script | Shift.Module); | ||
/** | ||
@@ -15,0 +15,0 @@ * Executes the modification. |
{ | ||
"name": "js-deobfuscator", | ||
"version": "1.1.4", | ||
"version": "1.1.6", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -195,2 +195,4 @@ # General purpose JavaScript deobfuscator | ||
interface Config { | ||
verbose: boolean; | ||
isModule: boolean; | ||
arrays: { | ||
@@ -222,3 +224,3 @@ unpackArrays: boolean; | ||
> js-deobfuscator -h | ||
Usage: run [options] | ||
Usage: js-deobfuscator [options] | ||
@@ -230,8 +232,6 @@ Deobfuscate a javascript file | ||
-o, --output [output_file] The deobfuscated output file (default: "output/output.js") | ||
-f, --force Whether to overwrite the output file or not | ||
-m, --module Parse an ESModule | ||
-h, --help display help for command | ||
> | ||
``` | ||
Alternatively use the online version at [deobfuscate.io](https://deobfuscate.io) |
1664346
0.24%105399
0.07%