restringer
Advanced tools
Comparing version 1.10.3 to 1.10.4
{ | ||
"name": "restringer", | ||
"version": "1.10.3", | ||
"version": "1.10.4", | ||
"description": "Deobfuscate Javascript with emphasis on reconstructing strings", | ||
@@ -14,3 +14,3 @@ "main": "index.js", | ||
"dependencies": { | ||
"flast": "^1.6.0", | ||
"flast": "^1.7.1", | ||
"isolated-vm": "^5.0.1", | ||
@@ -17,0 +17,0 @@ "jsdom": "^24.1.0", |
@@ -20,2 +20,3 @@ # Restringer | ||
* [Create Custom Deobfuscators](#create-custom-deobfuscators) | ||
* [Boilerplate Code for Starting from Scratch](#boilerplate-code-for-starting-from-scratch) | ||
* [Read More](#read-more) | ||
@@ -84,3 +85,3 @@ *** | ||
The basic structure of such a deobfuscator would be an array of deobfuscation modules | ||
(either [safe](src/modules/safe) or [unsafe](src/modules/unsafe)), run via the [runLoop](src/modules/utils/runLoop.js) util function. | ||
(either [safe](src/modules/safe) or [unsafe](src/modules/unsafe)), run via flAST's applyIteratively utility function. | ||
@@ -93,4 +94,4 @@ Unsafe modules run code through `eval` (using [isolated-vm](https://www.npmjs.com/package/isolated-vm) to be on the safe side) while safe modules do not. | ||
unsafe: {resolveDefiniteBinaryExpressions, resolveLocalCalls}, | ||
utils: {runLoop} | ||
} = require('restringer').deobModules; | ||
const {applyIteratively} = require('flast').utils; | ||
let script = 'obfuscated JS here'; | ||
@@ -102,3 +103,3 @@ const deobModules = [ | ||
]; | ||
script = runLoop(script, deobModules); | ||
script = applyIteratively(script, deobModules); | ||
console.log(script); // Deobfuscated script | ||
@@ -111,11 +112,11 @@ ``` | ||
unsafe: {resolveLocalCalls}, | ||
utils: {runLoop} | ||
} = require('restringer').deobModules; | ||
const {applyIteratively} = require('flast').utils; | ||
let script = 'obfuscated JS here'; | ||
// It's better to define a function with a name that can show up in the log (otherwise you'll get 'undefined') | ||
// It's better to define a function with a meaningful name that can show up in the log | ||
function resolveLocalCallsInGlobalScope(arb) { | ||
return resolveLocalCalls(arb, n => n.parentNode?.type === 'Program'); | ||
} | ||
script = runLoop(script, [resolveLocalCallsInGlobalScope]); | ||
script = applyIteratively(script, [resolveLocalCallsInGlobalScope]); | ||
console.log(script); // Deobfuscated script | ||
@@ -133,3 +134,3 @@ ``` | ||
// res.logger.setLogLevel(res.logger.logLevels.DEBUG); | ||
// res.logger.setLogLevelDebug(); | ||
res.detectObfuscationType = false; // Skip obfuscation type detection, including any pre and post processors | ||
@@ -148,2 +149,36 @@ | ||
``` | ||
*** | ||
### Boilerplate code for starting from scratch | ||
```javascript | ||
const {logger, applyIteratively, treeModifier} = require('flast').utils; | ||
// Optional loading from file | ||
// const fs = require('node:fs'); | ||
// const inputFilename = process.argv[2] || 'target.js'; | ||
// const code = fs.readFileSync(inputFilename, 'utf-8'); | ||
const code = `(function() { | ||
function createMessage() {return 'Hello' + ' ' + 'there!';} | ||
function print(msg) {console.log(msg);} | ||
print(createMessage()); | ||
})();`; | ||
logger.setLogLevelDebug(); | ||
let script = code; | ||
// Use this function to target the relevant nodes | ||
const f = n => n.type === 'Literal' && replacements[n.value]; | ||
// Use this function to modify the nodes according to your needs. | ||
// markNode(n) would delete the node, while markNode(n, {...}) would replace the node with the supplied node. | ||
const m = (n, arb) => arb.markNode(n, { | ||
type: 'Literal', | ||
value: replacements[n.value], | ||
}); | ||
const swc = treeModifier(f, m, 'StarWarsChanger'); | ||
script = applyIteratively(script, [swc]); | ||
if (code !== script) { | ||
console.log(script); | ||
// fs.writeFileSync(inputFilename + '-deob.js', script, 'utf-8'); | ||
} else console.log(`No changes`); | ||
``` | ||
*** | ||
@@ -150,0 +185,0 @@ |
@@ -1,5 +0,4 @@ | ||
const {generateFlatAST} = require('flast'); | ||
const logger = require(__dirname + '/../utils/logger'); | ||
const getCache = require(__dirname + '/../utils/getCache'); | ||
const generateHash = require(__dirname + '/../utils/generateHash'); | ||
const {generateFlatAST, utils: {logger}} = require('flast'); | ||
@@ -6,0 +5,0 @@ /** |
@@ -1,5 +0,4 @@ | ||
const {generateFlatAST} = require('flast'); | ||
const logger = require(__dirname + '/../utils/logger'); | ||
const getCache = require(__dirname + '/../utils/getCache'); | ||
const generateHash = require(__dirname + '/../utils/generateHash'); | ||
const {generateFlatAST, utils: {logger}} = require('flast'); | ||
@@ -6,0 +5,0 @@ /** |
@@ -1,2 +0,2 @@ | ||
const logger = require(__dirname + '/../utils/logger'); | ||
const {logger} = require('flast').utils; | ||
@@ -3,0 +3,0 @@ const minArrayLength = 20; |
@@ -0,3 +1,3 @@ | ||
const {logger} = require('flast').utils; | ||
const {badValue} = require(__dirname + '/../config'); | ||
const logger = require(__dirname + '/../utils/logger'); | ||
const Sandbox = require(__dirname + '/../utils/sandbox'); | ||
@@ -4,0 +4,0 @@ const evalInVm = require(__dirname + '/../utils/evalInVm'); |
@@ -0,3 +1,3 @@ | ||
const {logger} = require('flast').utils; | ||
const {badValue} = require(__dirname + '/../config'); | ||
const logger = require(__dirname + '/../utils/logger'); | ||
const Sandbox = require(__dirname + '/../utils/sandbox'); | ||
@@ -4,0 +4,0 @@ const evalInVm = require(__dirname + '/../utils/evalInVm'); |
@@ -1,5 +0,4 @@ | ||
const logger = require(__dirname + '/logger'); | ||
const {generateCode, parseCode} = require('flast'); | ||
const {badValue} = require(__dirname + '/../config'); | ||
const getObjType = require(__dirname + '/getObjType'); | ||
const {generateCode, parseCode, utils: {logger}} = require('flast'); | ||
@@ -6,0 +5,0 @@ /** |
@@ -0,5 +1,5 @@ | ||
const {logger} = require('flast').utils; | ||
const Sandbox = require(__dirname + '/sandbox'); | ||
const assert = require('node:assert'); | ||
const {badValue} = require(__dirname + '/../config'); | ||
const logger = require(__dirname + '/../utils/logger'); | ||
const getObjType = require(__dirname + '/../utils/getObjType'); | ||
@@ -6,0 +6,0 @@ const generateHash = require(__dirname + '/../utils/generateHash'); |
@@ -5,4 +5,5 @@ // noinspection HtmlRequiredLangAttribute,HtmlRequiredTitleElement | ||
const Sandbox = require(__dirname + '/sandbox'); | ||
// eslint-disable-next-line no-unused-vars | ||
const {JSDOM} = require('jsdom'); | ||
const logger = require(__dirname + '/../utils/logger'); | ||
const {logger} = require('flast').utils; | ||
const generateHash = require(__dirname + '/../utils/generateHash'); | ||
@@ -9,0 +10,0 @@ |
@@ -18,7 +18,5 @@ module.exports = { | ||
isNodeMarked: require(__dirname + '/isNodeMarked'), | ||
logger: require(__dirname + '/logger'), | ||
normalizeScript: require(__dirname + '/normalizeScript'), | ||
runLoop: require(__dirname + '/runLoop'), | ||
safeImplementations: require(__dirname + '/safeImplementations'), | ||
sandbox: require(__dirname + '/sandbox'), | ||
}; |
@@ -1,2 +0,2 @@ | ||
const runLoop = require(__dirname + '/runLoop'); | ||
const {applyIteratively} = require('flast').utils; | ||
const normalizeComputed = require(__dirname + '/../safe/normalizeComputed'); | ||
@@ -12,3 +12,3 @@ const normalizeEmptyStatements = require(__dirname + '/../safe/normalizeEmptyStatements'); | ||
function normalizeScript(script) { | ||
return runLoop(script, [ | ||
return applyIteratively(script, [ | ||
normalizeComputed, | ||
@@ -15,0 +15,0 @@ normalizeRedundantNotOperator, |
@@ -1,4 +0,3 @@ | ||
const {Arborist} = require('flast'); | ||
const {Arborist, utils: {logger}} = require('flast'); | ||
const generateHash = require(__dirname + '/generateHash'); | ||
const logger = require(__dirname + '/../utils/logger'); | ||
const {defaultMaxIterations, getGlobalMaxIterations} = require(__dirname + '/../config'); | ||
@@ -5,0 +4,0 @@ |
#!/usr/bin/env node | ||
const {logger, applyIteratively} = require('flast').utils; | ||
const processors = require(__dirname + '/processors'); | ||
@@ -7,5 +8,3 @@ const detectObfuscation = require('obfuscation-detector'); | ||
utils: { | ||
runLoop, | ||
normalizeScript, | ||
logger, | ||
}, | ||
@@ -37,3 +36,3 @@ safe, | ||
this.logger = logger; | ||
this.logger.setLogLevel(logger.logLevels.LOG); // Default log level | ||
this.logger.setLogLevelLog(); | ||
this.detectObfuscationType = true; | ||
@@ -111,3 +110,3 @@ // Deobfuscation methods that don't use eval | ||
this.modified = false; | ||
script = runLoop(this.script, this.safeMethods.concat(this.unsafeMethods)); | ||
script = applyIteratively(this.script, this.safeMethods.concat(this.unsafeMethods)); | ||
if (this.script !== script) { | ||
@@ -136,3 +135,3 @@ this.modified = true; | ||
if (this.modified && this.normalize) this.script = normalizeScript(this.script); | ||
if (clean) this.script = runLoop(this.script, [unsafe.removeDeadNodes]); | ||
if (clean) this.script = applyIteratively(this.script, [unsafe.removeDeadNodes]); | ||
return this.modified; | ||
@@ -149,3 +148,3 @@ } | ||
const processor = processors[i]; | ||
this.script = runLoop(this.script, [processor], 1); | ||
this.script = applyIteratively(this.script, [processor], 1); | ||
} | ||
@@ -166,4 +165,4 @@ } | ||
const restringer = new REstringer(content); | ||
if (args.quiet) restringer.logger.setLogLevel(logger.logLevels.NONE); | ||
else if (args.verbose) restringer.logger.setLogLevel(logger.logLevels.DEBUG); | ||
if (args.quiet) restringer.logger.setLogLevelNone(); | ||
else if (args.verbose) restringer.logger.setLogLevelDebug(); | ||
logger.log(`[!] REstringer v${REstringer.__version__}`); | ||
@@ -170,0 +169,0 @@ logger.log(`[!] Deobfuscating ${args.inputFilename}...`); |
@@ -15,3 +15,3 @@ const assert = require('node:assert'); | ||
function testCodeSample(testName, source, expected) { | ||
process.stdout.write(`Testing ${testName}... `); | ||
process.stdout.write(`${testName}... `); | ||
console.time('PASS'); | ||
@@ -36,3 +36,3 @@ const restringer = new REstringer(source); | ||
skippedTests++; | ||
console.log(`Testing [${moduleName}] ${test.name}...`.padEnd(101, '.') + ` SKIPPED: ${test.reason}`); | ||
console.log(`[${moduleName}] ${test.name}...`.padEnd(101, '.') + ` SKIPPED: ${test.reason}`); | ||
} | ||
@@ -39,0 +39,0 @@ } |
const assert = require('node:assert'); | ||
const {Arborist} = require('flast'); | ||
const {runLoop, logger} = require(__dirname + '/../src/modules').utils; | ||
const {logger, applyIteratively} = require('flast').utils; | ||
@@ -22,3 +22,3 @@ const tests = { | ||
function testModuleOnce(testName, testFunc, source, expected, prepTest = defaultPrepTest, prepRes = defaultPrepRes) { | ||
process.stdout.write(`Testing ${testName}... `); | ||
process.stdout.write(`${testName}... `); | ||
console.time('PASS'); | ||
@@ -42,6 +42,6 @@ const testInput = prepTest(source); | ||
function testModuleInLoop(testName, testFunc, source, expected, prepTest = null, prepRes = null) { | ||
process.stdout.write(`Testing ${testName}... `); | ||
process.stdout.write(`${testName}... `); | ||
console.time('PASS'); | ||
const testInput = prepTest ? prepTest(source) : source; | ||
const rawResult = runLoop(testInput, [testFunc]); | ||
const rawResult = applyIteratively(testInput, [testFunc]); | ||
const result = prepRes ? prepRes(rawResult) : rawResult; | ||
@@ -63,7 +63,7 @@ assert.deepEqual(result, expected); | ||
if (!test.looped) testModuleOnce(`[${moduleName}] ${test.name}`.padEnd(90, '.'), require(test.func), test.source, test.expected, test.prepareTest, test.prepareResult); | ||
// Tests will have the `isUtil` flag if they do not return an Arborist instance (i.e. can't use runLoop) | ||
// Tests will have the `isUtil` flag if they do not return an Arborist instance (i.e. can't use applyIteratively) | ||
if (!test.isUtil) testModuleInLoop(`[${moduleName}] ${test.name} (looped)`.padEnd(90, '.'), require(test.func), test.source, test.expected, test.prepareTest, test.prepareResult); | ||
} else { | ||
skippedTests++; | ||
console.log(`Testing [${moduleName}] ${test.name}...`.padEnd(101, '.') + ` SKIPPED: ${test.reason}`); | ||
console.log(`[${moduleName}] ${test.name}...`.padEnd(101, '.') + ` SKIPPED: ${test.reason}`); | ||
} | ||
@@ -70,0 +70,0 @@ } |
@@ -20,3 +20,3 @@ const fs = require('node:fs'); | ||
function testSampleDeobfuscation(testSampleName, testSampleFilename) { | ||
process.stdout.write(`Testing '${testSampleName}' obfuscated sample...`.padEnd(60, '.')); | ||
process.stdout.write(`'${testSampleName}' obfuscated sample...`.padEnd(60, '.')); | ||
console.time(' PASS'); | ||
@@ -23,0 +23,0 @@ const obfuscatedSource = fs.readFileSync(testSampleFilename, 'utf-8'); |
@@ -21,3 +21,3 @@ const {Arborist} = require('flast'); | ||
function testProcessor(testName, testProcs, source, expected, prepTest = defaultPrepTest, prepRes = defaultPrepRes) { | ||
process.stdout.write(`Testing ${testName}... `); | ||
process.stdout.write(`${testName}... `); | ||
console.time('PASS'); | ||
@@ -43,3 +43,3 @@ let rawRes = prepTest(source); | ||
skippedTests++; | ||
console.log(`Testing [${processorName}] ${test.name}...`.padEnd(101, '.') + ` SKIPPED: ${test.reason}`); | ||
console.log(`[${processorName}] ${test.name}...`.padEnd(101, '.') + ` SKIPPED: ${test.reason}`); | ||
} | ||
@@ -46,0 +46,0 @@ } |
@@ -14,3 +14,3 @@ const assert = require('node:assert'); | ||
function testCodeSample(testName, testFunc, verifyFunc) { | ||
process.stdout.write(`Testing ${testName}... `); | ||
process.stdout.write(`${testName}... `); | ||
console.time('PASS'); | ||
@@ -34,3 +34,3 @@ const results = testFunc(); | ||
skippedTests++; | ||
console.log(`Testing [${moduleName}] ${test.name}...`.padEnd(101, '.') + ` SKIPPED: ${test.reason}`); | ||
console.log(`[${moduleName}] ${test.name}...`.padEnd(101, '.') + ` SKIPPED: ${test.reason}`); | ||
} | ||
@@ -37,0 +37,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
674019
185
186
12196
Updatedflast@^1.7.1