restringer
v2.0.3
對 Javascript 進行反混淆並重建字串。盡可能簡化繁瑣的邏輯,同時遵守範圍限制。
在線嘗試@restringer.tech。
如需意見和建議,請隨時提出問題或在 Twitter 上找到我 - @ctrl__esc
npm install -g restringer
需要節點 16 或更高版本。
git clone [email protected]:PerimeterX/restringer.git
cd restringer
npm install
restringer.js 使用通用的反混淆方法來重建和恢復混淆的字串,並簡化只會造成阻礙的冗餘邏輯。 REstringer 使用混淆偵測器來識別需要應用特定反混淆方法的特定類型的混淆,以便規避反偵錯機製或其他阻止腳本反混淆的程式碼陷阱。
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.
範例:
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 是高度模組化的。它公開了允許創建可以解決特定問題的自訂反混淆器的模組。
這種反混淆器的基本結構是一系列反混淆模組(安全或不安全),透過 flAST 的 applyIteratively 實用程式函數運作。
不安全模組透過eval
運行程式碼(為了安全起見,使用isolated-vm),而安全模組則不然。
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
透過附加的candidateFilter
函數參數,可以縮小目標節點的範圍:
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
您還可以自訂任何反混淆方法,同時仍使用 REstringer,而無需自行執行循環:
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` ) ;