Desofusque Javascript e reconstrua strings. Simplifique a lógica complicada sempre que possível, respeitando as limitações de escopo.
Experimente online @ restringer.tech.
Para comentários e sugestões sinta-se à vontade para abrir um problema ou me encontrar no Twitter - @ctrl__esc
npm install -g restringer
Requer Nó 16 ou mais recente.
git clone [email protected]:PerimeterX/restringer.git
cd restringer
npm install
O restringer.js usa métodos genéricos de desofuscação que reconstroem e restauram strings ofuscadas e simplificam a lógica redundante destinada apenas a onerar. REstringer emprega o Detector de ofuscação para identificar tipos específicos de ofuscação para os quais há necessidade de aplicar métodos de desofuscação específicos para contornar mecanismos anti-depuração ou outras armadilhas de código que impedem a desofuscação do script.
Usage: restringer input_filename [-h] [-c] [-q | -v] [-m M] [-o [output_filename]]
positional arguments:
input_filename The obfuscated JS file
optional arguments:
-h, --help Show this help message and exit.
-c, --clean Remove dead nodes from script after deobfuscation is complete (unsafe).
-q, --quiet Suppress output to stdout. Output result only to stdout if the -o option is not set.
Does not go with the -v option.
-m, --max-iterations M Run at most M iterations
-v, --verbose Show more debug messages while deobfuscating. Does not go with the -q option.
-o, --output [output_filename] Write deobfuscated script to output_filename.
<input_filename>-deob.js is used if no filename is provided.
Exemplos:
restringer [target-file.js]
restringer [target-file.js] -o output.js
restringer [target-file.js] -v
restringer [target-file.js] -q
import { REstringer } from 'restringer' ;
const restringer = new REstringer ( '"RE" + "stringer"' ) ;
if ( restringer . deobfuscate ( ) ) {
console . log ( restringer . script ) ;
} else {
console . log ( 'Nothing was deobfuscated :/' ) ;
}
// Output: 'REstringer';
REstringer é altamente modularizado. Expõe módulos que permitem a criação de desofuscadores customizados que podem resolver problemas específicos.
A estrutura básica de tal desofuscador seria uma matriz de módulos de desofuscação (seguros ou inseguros), executados por meio da função de utilitário applyIterativamente do flAST.
Módulos inseguros executam código por meio de eval
(usando VM isolada para estar no lado seguro), enquanto módulos seguros não.
import { applyIteratively } from 'flast' ;
import { safe , unsafe } from 'restringer' ;
const { normalizeComputed } = safe ;
const { resolveDefiniteBinaryExpressions , resolveLocalCalls } = unsafe ;
let script = 'obfuscated JS here' ;
const deobModules = [
resolveDefiniteBinaryExpressions ,
resolveLocalCalls ,
normalizeComputed ,
] ;
script = applyIteratively ( script , deobModules ) ;
console . log ( script ) ; // Deobfuscated script
Com o argumento adicional da função candidateFilter
, é possível restringir os nós de destino:
import { unsafe } from 'restringer' ;
const { resolveLocalCalls } = unsafe ;
import { applyIteratively } from 'flast' ;
let script = 'obfuscated JS here' ;
// 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 = applyIteratively ( script , [ resolveLocalCallsInGlobalScope ] ) ;
console . log ( script ) ; // Deobfuscated script
Você também pode personalizar qualquer método de desofuscação enquanto ainda usa o REstringer sem executar o loop sozinho:
import fs from 'node:fs' ;
import { REstringer } from 'restringer' ;
const inputFilename = process . argv [ 2 ] ;
const code = fs . readFileSync ( inputFilename , 'utf-8' ) ;
const res = new REstringer ( code ) ;
// res.logger.setLogLevelDebug();
res . detectObfuscationType = false ; // Skip obfuscation type detection, including any pre and post processors
const targetFunc = res . unsafeMethods . find ( m => m . name === 'resolveLocalCalls' ) ;
let changes = 0 ; // Resolve only the first 5 calls
res . safeMethods [ res . unsafeMethods . indexOf ( targetFunc ) ] = function customResolveLocalCalls ( n ) { return targetFunc ( n , ( ) => changes ++ < 5 ) }
res . deobfuscate ( ) ;
if ( res . script !== code ) {
console . log ( '[+] Deob successful' ) ;
fs . writeFileSync ( ` ${ inputFilename } -deob.js` , res . script , 'utf-8' ) ;
} else console . log ( '[-] Nothing deobfuscated :/' ) ;
import { applyIteratively , logger } from 'flast' ;
// Optional loading from file
// import fs from '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 ( ) ;
/**
* Replace specific strings with other strings
* @param {Arborist} arb
* @return {Arborist}
*/
function replaceSpecificLiterals ( arb ) {
const replacements = {
'Hello' : 'General' ,
'there!' : 'Kenobi!' ,
} ;
// Iterate over only the relevant nodes by targeting specific types using the typeMap property on the root node
const relevantNodes = [
... ( arb . ast [ 0 ] . typeMap . Literal || [ ] ) ,
// ...(arb.ast.typeMap.TemplateLiteral || []), // unnecessary for this example, but this is how to add more types
] ;
for ( const n of relevantNodes ) {
if ( replacements [ n . value ] ) {
// dynamically define a replacement node by creating an object with a type and value properties
// markNode(n) would delete the node, while markNode(n, {...}) would replace the node with the supplied node.
arb . markNode ( n , { type : 'Literal' , value : replacements [ n . value ] } ) ;
}
}
return arb ;
}
let script = code ;
script = applyIteratively ( script , [
replaceSpecificLiterals ,
] ) ;
if ( code !== script ) {
console . log ( script ) ;
// fs.writeFileSync(inputFilename + '-deob.js', script, 'utf-8');
} else console . log ( `No changes` ) ;