restringer
v2.0.3
Javascript를 난독 해제하고 문자열을 재구성합니다. 범위 제한을 준수하면서 가능한 경우 번거로운 논리를 단순화합니다.
온라인 @restinger.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
통해 코드를 실행하지만(안전한 측면에서 격리된 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` ) ;