ลดความยุ่งเหยิงของ 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 เป็นแบบโมดูลาร์สูง โดยจะเปิดเผยโมดูลที่อนุญาตให้สร้างตัวลดความสับสนที่กำหนดเองซึ่งสามารถแก้ไขปัญหาเฉพาะได้
โครงสร้างพื้นฐานของตัวลดความยุ่งเหยิงดังกล่าวจะเป็นอาร์เรย์ของโมดูลลดความยุ่งเหยิง (ไม่ว่าจะปลอดภัยหรือไม่ปลอดภัย) ซึ่งทำงานผ่านฟังก์ชันอรรถประโยชน์ ApplyIteratively ของ flAST
โมดูลที่ไม่ปลอดภัยจะรันโค้ดผ่าน 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` ) ;